Skip to content

feat: cart tab - add form flow #797

Open
santipalenque wants to merge 7 commits intomasterfrom
feature/add-form-to-cart
Open

feat: cart tab - add form flow #797
santipalenque wants to merge 7 commits intomasterfrom
feature/add-form-to-cart

Conversation

@santipalenque
Copy link

@santipalenque santipalenque commented Feb 19, 2026

https://app.clickup.com/t/86b66n5kf

Summary by CodeRabbit

  • New Features
    • Full sponsor cart UI: browse/select sponsor forms, choose add‑ons, add to cart, edit items, and checkout options.
    • Rich, editable form items with per-item fields, global quantities, dynamic rate handling, discounts, and notes/settings modals.
    • New input controls: enhanced dropdowns, timepicker, price/discount fields, and addon selectors.
  • Localization
    • Added cart-related translation keys and messages.
  • Tests
    • Comprehensive test suites for form table and select-dialog interactions.

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A sponsor cart feature set was added: search/select sponsor forms, add forms to cart, edit form items with Formik-driven dynamic fields and rate/quantity calculations, new Formik-MUI input components, modals for item settings/notes, reducer/actions for cart state, and comprehensive tests and i18n entries.

Changes

Cohort / File(s) Summary
Sponsor queries & cart actions
src/actions/sponsor-actions.js, src/actions/sponsor-cart-actions.js
Refactored querySummitAddons to a non-debounced async function (signature changed). Added cart-related action constants and thunks: getSponsorFormsForCart, getSponsorForm, addCartForm with pagination, normalization, error handling, and new exported constants.
Cart UI & pages
src/pages/sponsors/sponsor-cart-tab/index.js, src/pages/sponsors/edit-sponsor-page.js, src/pages/sponsors/sponsor-cart-tab/components/cart-view.js, src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/..., src/pages/sponsors/sponsor-cart-tab/components/edit-form/...
New CartView, SelectFormDialog, EditForm, NewCartForm components; SponsorCartTab refactored to local state composition (SelectFormDialog, CartView, NewCartForm). EditSponsorPage wired to dispatch purchases metadata.
Reducers / state
src/reducers/sponsors/sponsor-page-cart-list-reducer.js, src/utils/constants.js
Reducer expanded with availableForms and sponsorForm state and handlers for new request/receive actions. Added DISCOUNT_TYPES constant.
Form item table & item components
src/components/mui/FormItemTable/index.js, src/components/mui/FormItemTable/components/ItemTableField.js, src/components/mui/FormItemTable/components/GlobalQuantityField.js, src/components/mui/ItemSettingsModal/index.js, src/components/mui/NotesModal/index.js
Added FormItemTable rendering dynamic meta-fields, rate computation, totals, and per-item actions. New ItemTableField and GlobalQuantityField for various field types. ItemSettingsModal and NotesModal provide per-item editing UIs.
Formik-MUI inputs & addon selects
src/components/mui/formik-inputs/*, src/components/mui/summit-addon-select.js, src/components/mui/sponsor-addon-select.js, src/components/forms/sponsor-general-form/manage-tier-addons-popup.js
Added many Formik-aware inputs: Timepicker, DropdownCheckbox, DropdownRadio, DiscountField, PriceField (stateful), MuiFormikSummitAddonSelect; introduced standalone SummitAddonSelect and SponsorAddonSelect components. Replaced SummitAddonSelect usage with MuiFormikSummitAddonSelect in manage-tier-addons-popup.
Styling & theme
src/components/CustomTheme.js
Adjusted MuiFormHelperText positioning (absolute top:100%, marginTop:4px) affecting helper text layout.
i18n
src/i18n/en.json
Added cart-related translation keys (items, add form, select addon, edit form fields, discount/total labels) and several validation messages.
Tests
src/components/mui/FormItemTable/__tests__/FormItemTable.test.js, src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js
Extensive Jest tests for FormItemTable and SelectFormDialog covering rendering, interactions, validation, search/sort/pagination, and empty states.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Dialog as SelectFormDialog
    participant Redux as Redux Store
    participant API as API Server

    User->>Dialog: Open dialog
    Dialog->>Redux: dispatch getSponsorFormsForCart(term,page=1)
    Redux->>API: GET /show-forms?term=...&page=1&per_page=...
    API-->>Redux: 200 { forms }
    Redux-->>Dialog: update availableForms
    Dialog-->>User: show forms

    User->>Dialog: Select form + addon
    User->>Dialog: Click Save
    Dialog->>Redux: dispatch getSponsorForm(formId)
    Redux->>API: GET /sponsor-forms/:formId
    API-->>Redux: 200 { sponsorForm with meta_fields }
    Redux-->>Dialog: sponsorForm stored
    Dialog-->>User: close and call onSave(form, addon)
Loading
sequenceDiagram
    actor User
    participant EditForm as EditForm (Formik)
    participant Modals as NotesModal / ItemSettingsModal
    participant Redux as Redux Store
    participant API as API Server

    User->>EditForm: load form for editing
    EditForm->>EditForm: build Formik initialValues + Yup schema
    EditForm-->>User: render FormItemTable

    User->>EditForm: change quantity or rate
    EditForm->>EditForm: update Formik value, recalc totals
    EditForm-->>User: show updated totals

    User->>EditForm: open ItemSettingsModal
    User->>Modals: change meta-field
    Modals->>EditForm: update Formik field
    Modals-->>EditForm: close

    User->>EditForm: Save
    EditForm->>Redux: dispatch addCartForm(formId, addOnId, payload)
    Redux->>API: POST /sponsors/.../forms
    API-->>Redux: 201 { created cart form }
    Redux-->>EditForm: update cart state
    EditForm-->>User: close editor
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • smarcet
  • tomrndom
  • romanetar

Poem

🐰 I hopped through forms and fields today,
Built carts that bounce and totals that sway,
Meta-flower petals, rates in line,
Notes and settings snugly fine,
A little carrot-coded hooray! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: cart tab - add form flow' accurately describes the main objective of adding a form submission flow to the sponsor cart tab, which is reflected throughout the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/add-form-to-cart

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (12)
src/components/mui/sponsor-addon-select.js-14-26 (1)

14-26: ⚠️ Potential issue | 🟡 Minor

Refetch when sponsor/summit props change and guard missing sponsorships.

Lines 14-26: The effect runs only once with an empty dependency array, so changes to sponsor or summitId won't trigger a refetch. Additionally, accessing sponsor.sponsorships without optional chaining will crash if sponsorships is unset. The debug log should also be removed.

🔧 Suggested fix
-  const sponsorshipIds = sponsor.sponsorships.map((e) => e.id);
+  const sponsorshipIds = sponsor?.sponsorships?.map((e) => e.id) ?? [];

   useEffect(() => {
-    querySponsorAddons(summitId, sponsor.id, sponsorshipIds,(results) => {
-      console.log("RESULTS: ", results);
+    if (!sponsor?.id || sponsorshipIds.length === 0) {
+      setOptions([]);
+      return;
+    }
+    querySponsorAddons(summitId, sponsor.id, sponsorshipIds, (results) => {
       const normalized = results.map((r) => ({
         value: r.id,
         label: r.name
       }));
       setOptions(normalized);
     });
-  }, []);
+  }, [summitId, sponsor?.id, sponsorshipIds.join(",")]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/sponsor-addon-select.js` around lines 14 - 26, The effect
currently runs only once and reads sponsor.sponsorships unsafely and logs debug
output; fix by moving the sponsorshipIds computation into the useEffect, guard
sponsorships with optional chaining or a default (e.g., const sponsorshipIds =
sponsor?.sponsorships?.map(e => e.id) || []), remove the console.log, and update
the useEffect dependency array to include summitId and the sponsor (or
sponsor.id and sponsor.sponsorships) so querySponsorAddons is re-run when those
props change; keep setOptions and the normalized mapping as-is and call
querySponsorAddons(summitId, sponsor.id, sponsorshipIds, ...) inside the updated
effect.
src/components/mui/summit-addon-select.js-15-23 (1)

15-23: ⚠️ Potential issue | 🟡 Minor

Add summitId to the effect dependency array to refetch addons when the summit changes.

Lines 15-23: The effect runs only on mount with an empty dependency array, so if the summitId prop changes, the component will display stale addon options. Add summitId as a dependency to ensure the effect re-runs when the summit changes.

🔧 Suggested fix
  useEffect(() => {
    querySummitAddons(summitId, (results) => {
      const normalized = results.map((r) => ({
        value: r,
        label: r
      }));
      setOptions(normalized);
    });
-  }, []);
+  }, [summitId]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/summit-addon-select.js` around lines 15 - 23, The
useEffect that calls querySummitAddons currently has an empty dependency array
and will not refetch when summitId changes; update the effect for the component
(the useEffect that invokes querySummitAddons and calls setOptions) to include
summitId in its dependency array so the effect re-runs whenever the summitId
prop changes, ensuring options are reloaded for the new summit.
src/pages/sponsors/sponsor-cart-tab/components/cart-view.js-47-49 (1)

47-49: ⚠️ Potential issue | 🟡 Minor

Remove or convert debug console.log statements.

Multiple handlers contain placeholder console.log statements that should be removed or replaced with actual implementation before merging. This aligns with the PR title noting "WIP".

Also applies to: 59-65

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/cart-view.js` around lines 47
- 49, The debug console.log in handleManageItems should be removed and replaced
with the real implementation or a call to the existing cart update handlers;
specifically update the handleManageItems function to perform the intended
action (e.g., open a manage modal, dispatch an action, or call functions like
removeItem/updateQuantity) instead of logging, and likewise remove/replace any
other placeholder console.log statements in the nearby handlers (the handlers
around lines 59-65) with their proper implementations or calls to shared
utilities so no debug logs remain.
src/components/mui/FormItemTable/components/ItemTableField.js-13-79 (1)

13-79: ⚠️ Potential issue | 🟡 Minor

Missing default case in switch statement.

The switch statement doesn't handle unknown field.type values, causing the function to implicitly return undefined. This could cause silent rendering failures for unrecognized field types.

Proposed fix to add default case
     case "TextArea":
       return (
         <MuiFormikTextField
           name={name}
           label={label}
           fullWidth
           size="small"
           multiline
           rows={3}
         />
       );
+    default:
+      console.warn(`Unknown field type: ${field.type}`);
+      return null;
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/components/ItemTableField.js` around lines
13 - 79, The switch over field.type in the ItemTableField component lacks a
default branch, so unknown field.type values cause an implicit undefined render;
add a default case to the switch (in the same switch handling "CheckBox",
"CheckBoxList", "RadioButtonList", etc.) that returns a safe fallback (e.g.,
null) and logs a warning with the offending field.type (use console.warn or your
app logger) so unrecognized types fail loudly instead of producing silent
rendering errors.
src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js-16-18 (1)

16-18: ⚠️ Potential issue | 🟡 Minor

Edge case: allSelected is true when options is empty.

Array.every() returns true for an empty array. If options is empty, allSelected will be true, causing the "All" checkbox to appear checked and renderValue to show "All" text even though nothing is actually selected.

Proposed fix
-  const allSelected = options.every(({ value }) =>
-    field.value?.includes(value)
-  );
+  const allSelected = options.length > 0 && options.every(({ value }) =>
+    field.value?.includes(value)
+  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js` around
lines 16 - 18, The bug is that allSelected is true when options is empty because
Array.every returns true for empty arrays; update the allSelected calculation in
mui-formik-dropdown-checkbox.js (the allSelected constant) to require
options.length > 0 and that field.value is an array before checking every option
(e.g., guard with options.length > 0 && Array.isArray(field.value) &&
options.every(({ value }) => field.value.includes(value))) so the "All" checkbox
and renderValue only show "All" when there are options and they are actually all
selected.
src/pages/sponsors/sponsor-cart-tab/components/edit-form.js-1-9 (1)

1-9: ⚠️ Potential issue | 🟡 Minor

Placeholder file with unused imports should be removed or repurposed.

This file defines an EditForm component that returns null and imports unused React and T. Meanwhile, the actual EditForm implementation exists at src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js. This placeholder may cause import confusion.

If this file is intended as a barrel/re-export, update it to re-export from the actual implementation. Otherwise, consider removing it.

Option 1: Convert to re-export
-import React from "react";
-import T from "i18n-react/dist/i18n-react";
-
-const EditForm = () => {
-
-  return null;
-}
-
-export default EditForm;
+export { default } from "./edit-form/edit-form";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form.js` around lines 1 -
9, This placeholder EditForm component imports React and T but returns null and
shadows the real implementation; either remove this file and update any imports
to point to the real component, or turn it into a simple re-export of the real
implementation (stop importing unused React/T and export the default from the
actual edit-form implementation so consumers resolve correctly). Locate the
symbol EditForm in this file and replace the body with a re-export of the real
implementation (or delete the file) and run a quick search to update any broken
imports.
src/components/mui/FormItemTable/components/GlobalQuantityField.js-14-17 (1)

14-17: ⚠️ Potential issue | 🟡 Minor

Missing helpers in useEffect dependency array.

The effect uses helpers.setValue and helpers.setTouched but doesn't include helpers in the dependency array. This could cause stale closure issues. Additionally, unconditionally setting touched: true on value changes may display validation errors before user interaction.

Proposed fix
   useEffect(() => {
     helpers.setValue(value);
-    helpers.setTouched(true);
-  }, [value]);
+    // Only mark as touched if the value actually changed from user interaction
+  }, [value, helpers]);

Note: The eslint-disable comment suggests this was intentionally omitted, but could cause issues if helpers reference changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/components/GlobalQuantityField.js` around
lines 14 - 17, The effect currently uses helpers.setValue and helpers.setTouched
but only lists [value] as a dependency which can lead to stale closures; update
the useEffect to include helpers in its dependency array (e.g., [value,
helpers]) and stop unconditionally calling helpers.setTouched(true) on every
value change — either remove the setTouched call so programmatic updates don't
mark the field touched, or guard it behind an explicit condition/prop (e.g.,
only mark touched for user-driven changes). Ensure you update the effect around
the existing useEffect that references helpers.setValue and helpers.setTouched.
src/pages/sponsors/sponsor-cart-tab/components/cart-view.js-136-136 (1)

136-136: ⚠️ Potential issue | 🟡 Minor

Hardcoded English string breaks internationalization.

The text "forms in Cart" is hardcoded while the rest of the component uses T.translate(). This should use a translation key for consistency.

Proposed fix
-          <Box component="span">{cart?.forms.length} forms in Cart</Box>
+          <Box component="span">
+            {T.translate("edit_sponsor.cart_tab.forms_in_cart", { count: cart?.forms.length })}
+          </Box>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/cart-view.js` at line 136,
Replace the hardcoded English label inside the Box component so it uses the app
i18n helper like the other strings: call T.translate with an appropriate key
(e.g., "sponsors.cart.formsInCart") and interpolate cart?.forms.length into that
localized message instead of the literal "forms in Cart"; update the usage near
the Box component that currently renders {cart?.forms.length} forms in Cart to
use T.translate(...) and pass the count as a variable so translations can render
pluralization/localized text correctly.
src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js-57-64 (1)

57-64: ⚠️ Potential issue | 🟡 Minor

State not reset when dialog closes via backdrop/escape.

The Dialog's onClose prop calls onClose directly (Line 111), bypassing handleClose which resets selectedRows. Additionally, selectedAddon is never reset. This causes stale selections when the dialog reopens.

🐛 Proposed fix
   const handleClose = () => {
     setSelectedRows([]);
+    setSelectedAddon(null);
     onClose();
   };

   // ...

-    <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
+    <Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>

Also applies to: 111-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`
around lines 57 - 64, The Dialog's onClose currently calls onClose directly,
bypassing handleClose so selectedRows and selectedAddon aren't reset; change the
Dialog to use handleClose for its onClose handler and update handleClose (and
any other close paths like confirm/cancel handlers) to reset both selectedRows
and selectedAddon (e.g., setSelectedRows([]) and
setSelectedAddon(null/initialValue)) before calling onClose(), and ensure
handleOnCheck continues to set selectedRows as before.
src/components/mui/FormItemTable/__tests__/FormItemTable.test.js-446-452 (1)

446-452: ⚠️ Potential issue | 🟡 Minor

Property name mismatch in test mock data.

The test uses class: "Form" but the ItemTableField component constructs the field name using field.class_field (see relevant snippet line 10: const name = \i-${rowId}-c-${field.class_field}-f-${field.type_id}`;`). This inconsistency will cause the test to not render inputs with the expected test IDs.

🐛 Proposed fix
           meta_fields: [
             {
               type_id: 100,
-              class: "Form",
+              class_field: "Form",
               name: "Description",
               type: "Text"
             }
           ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 446 - 452, The mock field in the test uses "class" whereas the component
(ItemTableField) reads field.class_field to build the input name (const name =
`i-${rowId}-c-${field.class_field}-f-${field.type_id}`), so update the test
fixture to include a class_field property (e.g. class_field: "Form") or rename
the existing "class" key to "class_field" so the generated test IDs match and
the inputs render correctly.
src/components/mui/FormItemTable/__tests__/FormItemTable.test.js-19-23 (1)

19-23: ⚠️ Potential issue | 🟡 Minor

Property name mismatch in MOCK_RATE_DATES.

The mock uses onsite_end_date but the FormItemTable component expects onsite_price_end_date based on the relevant code snippet (lines 45-47 in FormItemTable/index.js). This mismatch will cause the onsite rate logic to behave unexpectedly in tests.

🐛 Proposed fix
 const MOCK_RATE_DATES = {
   early_bird_end_date: EARLY_BIRD_DATE,
   standard_price_end_date: STANDARD_DATE,
-  onsite_end_date: ONSITE_DATE
+  onsite_price_end_date: ONSITE_DATE
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 19 - 23, The mock object MOCK_RATE_DATES uses the wrong property name for
the onsite date; update MOCK_RATE_DATES so the key matches what FormItemTable
expects (onsite_price_end_date) instead of onsite_end_date, keeping
EARLY_BIRD_DATE and STANDARD_DATE mapped to early_bird_end_date and
standard_price_end_date respectively; this ensures the FormItemTable logic that
reads onsite_price_end_date will get ONSITE_DATE from the test fixture.
src/components/mui/FormItemTable/__tests__/FormItemTable.test.js-558-578 (1)

558-578: ⚠️ Potential issue | 🟡 Minor

Incorrect property name used in rate highlighting tests.

The tests use MOCK_RATE_DATES.early_bird_rate and MOCK_RATE_DATES.standard_rate, but MOCK_RATE_DATES is defined with early_bird_end_date and standard_price_end_date properties. This will result in accessing undefined values, causing the tests to not behave as intended.

🐛 Proposed fix
     it("highlights standard rate when current time is between early_bird and standard", () => {
       jest
         .spyOn(Date, "now")
         .mockImplementation(
           () =>
-            (MOCK_RATE_DATES.early_bird_rate + TIME_OFFSET) *
+            (MOCK_RATE_DATES.early_bird_end_date + TIME_OFFSET) *
             MILLISECONDS_MULTIPLIER
         );
       // ...
     });

     it("highlights onsite rate when current time is after standard_rate", () => {
       jest
         .spyOn(Date, "now")
         .mockImplementation(
           () =>
-            (MOCK_RATE_DATES.standard_rate + TIME_OFFSET) *
+            (MOCK_RATE_DATES.standard_price_end_date + TIME_OFFSET) *
             MILLISECONDS_MULTIPLIER
         );
       // ...
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 558 - 578, The test is referencing non-existent properties on
MOCK_RATE_DATES (early_bird_rate and standard_rate); update the test to use the
correct properties (early_bird_end_date and standard_price_end_date) when
building the mocked Date.now value used in the "highlights standard rate..."
spec (the Date.now mock in the test and any other tests referencing those wrong
keys), or alternatively adjust MOCK_RATE_DATES to include the expected keys;
ensure the Date.now mock multiplies the correct end-date field by
MILLISECONDS_MULTIPLIER so the time comparison in FormItemTable rendering uses
real values.
🧹 Nitpick comments (8)
src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js (1)

126-129: Use .forEach() instead of .map() for side effects.

Using .map() when the return value is not used is misleading. These iterations are purely for side effects (building accumulator objects), so .forEach() is more appropriate.

Proposed fix
 const buildInitialValues = (items, timeZone) =>
   items.reduce((acc, item) => {
-    item.meta_fields.map((f) => {
+    item.meta_fields.forEach((f) => {
       acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
         parseValue(f, timeZone);
     });
     // ...
   }, {});

 const buildValidationSchema = (items, t) =>
   items.reduce((acc, item) => {
     item.meta_fields
       .filter((f) => f.class_field === "Form")
-      .map((f) => {
+      .forEach((f) => {
         acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
           getYupValidation(f, t);
       });
     // ...
   }, {});

Also applies to: 142-145

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js` around
lines 126 - 129, Replace uses of .map() that are used only for side effects with
.forEach() to avoid misleading code: change item.meta_fields.map((f) => { ... })
to item.meta_fields.forEach((f) => { ... }) in the accumulator-building blocks
(the snippet using
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = parseValue(f,
timeZone);) and apply the same change for the similar occurrence around the
other block (the code at the second occurrence that mirrors lines 142-145).
src/components/mui/FormItemTable/index.js (2)

80-86: calculateTotal is not memoized, causing potential performance issues.

calculateTotal is a regular function that gets recreated on every render and is used in totalAmount useMemo. Since it's not stable, changes to unrelated state could trigger unnecessary recalculations.

Consider memoizing calculateTotal
-  const calculateTotal = (row) => {
+  const calculateTotal = useCallback((row) => {
     const qty =
       values[`i-${row.form_item_id}-c-global-f-quantity`] ||
       calculateQuantity(row);
     if (currentApplicableRate === "expired") return 0;
     return qty * row.rates[currentApplicableRate];
-  };
+  }, [values, calculateQuantity, currentApplicableRate]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 80 - 86,
calculateTotal is recreated each render which invalidates the totalAmount
useMemo; wrap calculateTotal in React.useCallback so it is stable between
renders (keep its signature calculateTotal(row)), and include as dependencies:
currentApplicableRate, values, and calculateQuantity (and any other helper it
uses). Ensure you reference the same calculateTotal in the totalAmount useMemo
so the memo depends on the stable callback rather than a new function each
render.

64-78: calculateQuantity dependency on valuesStr may cause stale closures.

The useCallback depends on valuesStr (stringified values) but references extraColumns directly. If extraColumns changes without valuesStr changing, the callback will use stale extraColumns. Also, stringifying the entire values object on every render for dependency comparison is inefficient.

Proposed fix
   const calculateQuantity = useCallback(
     (row) => {
       const qtyEXC = extraColumns.filter((exc) => exc.type === "Quantity");
       return qtyEXC.reduce((res, exc) => {
         const start = res > 0 ? res : 1;
         return (
           start *
           (values?.[
             `i-${row.form_item_id}-c-${exc.class_field}-f-${exc.type_id}`
           ] || 0)
         );
       }, 0);
     },
-    [valuesStr]
+    [values, extraColumns]
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 64 - 78, The
calculateQuantity callback currently lists only valuesStr as a dependency and
reads extraColumns and values directly, which can produce stale closures; update
the useCallback dependencies to include extraColumns and values (or a stable
memoized reference to values) instead of relying on valuesStr stringification,
or memoize extraColumns where it’s created and then include that memoized
symbol; ensure the dependency array is [values, extraColumns] (or
[memoizedValues, memoizedExtraColumns]) so calculateQuantity always sees the
latest extraColumns and values.
src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js (1)

35-72: Missing validation error display for users.

The component binds error={meta.touched && Boolean(meta.error)} to FormControl, which sets error styling, but doesn't render the actual error message. Other similar components (e.g., MuiFormikCheckbox) include a FormHelperText to display the error text.

Proposed fix to display validation errors
+import {
+  Checkbox,
+  Divider,
+  FormControl,
+  FormHelperText,
+  ListItemText,
+  MenuItem,
+  Select
+} from "@mui/material";
...
       </Select>
+      {meta.touched && meta.error && (
+        <FormHelperText>{meta.error}</FormHelperText>
+      )}
     </FormControl>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js` around
lines 35 - 72, The FormControl is marking errors via error={meta.touched &&
Boolean(meta.error)} but the component (MuiFormikDropdownCheckbox) never renders
the error text; add a FormHelperText under the Select that displays the
validation message when meta.touched && meta.error is truthy (e.g.,
<FormHelperText>{meta.touched && meta.error}</FormHelperText>), and ensure
FormHelperText is imported from `@mui/material` so the visual error text matches
the FormControl error state.
src/pages/sponsors/sponsor-cart-tab/components/cart-view.js (1)

35-37: Missing getSponsorCart in useEffect dependency array.

The effect calls getSponsorCart() but doesn't include it in the dependency array. While this works due to how connect works, adding it follows React hooks best practices and prevents lint warnings.

Proposed fix
   useEffect(() => {
     getSponsorCart();
-  }, []);
+  }, [getSponsorCart]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/cart-view.js` around lines 35
- 37, The useEffect invoking getSponsorCart is missing getSponsorCart in its
dependency array; update the useEffect that calls getSponsorCart to include
getSponsorCart in the deps so React hooks linters are satisfied, and if
getSponsorCart is not a stable reference (e.g., coming from props via
mapDispatchToProps or recreated each render) make it stable by memoizing it
(move to a stable mapDispatchToProps, wrap it in useCallback where defined, or
otherwise ensure the reference is stable) before adding to the dependency array.
src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js (2)

173-177: Incomplete PropTypes definition.

Several props are used but not declared in PropTypes: availableForms, sponsor, open, and getSponsorFormsForCart. This reduces type safety and developer experience.

♻️ Suggested improvement
 SelectFormDialog.propTypes = {
+  availableForms: PropTypes.shape({
+    forms: PropTypes.array,
+    currentPage: PropTypes.number,
+    term: PropTypes.string,
+    order: PropTypes.string,
+    orderDir: PropTypes.number,
+    total: PropTypes.number
+  }).isRequired,
   summitId: PropTypes.number.isRequired,
+  sponsor: PropTypes.shape({}).isRequired,
+  open: PropTypes.bool.isRequired,
   onClose: PropTypes.func.isRequired,
-  onSave: PropTypes.func.isRequired
+  onSave: PropTypes.func.isRequired,
+  getSponsorFormsForCart: PropTypes.func.isRequired
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`
around lines 173 - 177, SelectFormDialog.propTypes is missing several props used
by the component; add PropTypes entries for availableForms (array.isRequired or
array), sponsor (object.isRequired or shape), open (bool.isRequired), and
getSponsorFormsForCart (func.isRequired) to match how they're used. Update the
SelectFormDialog.propTypes declaration to include these keys with appropriate
PropTypes types (e.g., PropTypes.array, PropTypes.object or PropTypes.shape,
PropTypes.bool, PropTypes.func) and mark as required where the component depends
on them.

38-40: Missing dependency in useEffect.

The getSponsorFormsForCart function from props should be included in the dependency array. While it's a Redux-bound action creator that typically doesn't change, including it follows React's exhaustive-deps rule and prevents potential stale closure issues.

♻️ Suggested improvement
   useEffect(() => {
     getSponsorFormsForCart();
-  }, []);
+  }, [getSponsorFormsForCart]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`
around lines 38 - 40, The useEffect that calls getSponsorFormsForCart is missing
getSponsorFormsForCart in its dependency array; update the effect in
select-form-dialog's component so the dependency array includes
getSponsorFormsForCart (or alternatively ensure the prop is stable by wrapping
the action in useCallback where it's defined) to satisfy exhaustive-deps and
avoid stale closures—specifically modify the useEffect that currently calls
getSponsorFormsForCart() to depend on getSponsorFormsForCart.
src/pages/sponsors/sponsor-cart-tab/index.js (1)

24-29: No mechanism to exit edit mode.

There's no handler to set formEdit back to null after editing is complete or cancelled. The EditForm component's cancel button calls navigate(-1) (based on relevant snippet), which navigates away rather than returning to the cart view within this tab.

♻️ Suggested approach

Consider adding an onCancel or onComplete callback to EditForm that resets formEdit:

+  const handleEditComplete = () => {
+    setFormEdit(null);
+  };

   // ...

-      {formEdit && <EditForm form={formEdit} />}
+      {formEdit && (
+        <EditForm
+          form={formEdit.form}
+          addOnId={formEdit.addOnId}
+          onCancel={handleEditComplete}
+          onSave={handleEditComplete}
+        />
+      )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/index.js` around lines 24 - 29, The
component lacks a way to exit edit mode because formEdit is never reset; add a
handler (e.g., handleCloseEdit or handleCancelEdit) that calls setFormEdit(null)
and pass it into the EditForm as an onCancel/onComplete prop so the EditForm can
invoke it when the user cancels or finishes editing; also update EditForm usage
to call this callback instead of relying on navigate(-1) (or call
setFormEdit(null) alongside navigation) and ensure handleAddForm still sets
formEdit({ form, addOnId }) as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/actions/sponsor-cart-actions.js`:
- Around line 226-261: In saveCartForm, the request is using the wrong base URL,
missing sponsorId, and returns an incorrect success message—retrieve sponsorId
from currentSponsorState (similar to other cart actions), switch the request
base to window.PURCHASES_API_URL, and post to the path
/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/carts/current/forms/${formId}
(keep the normalizedEntity payload), then replace the snackbarSuccessHandler
translation key sponsor_list.sponsor_added with the correct cart-form success
key used elsewhere in this file; ensure the postRequest call still uses
createAction(FORM_CART_SAVED) and existing error handler.

In `@src/components/mui/FormItemTable/components/GlobalQuantityField.js`:
- Around line 25-32: The slotProps currently use slotProps.input for native
input attributes in GlobalQuantityField (and the Quantity case in
ItemTableField); change those to slotProps.htmlInput so native attributes
(readOnly, min, max) are applied to the underlying <input> element—update the
slotProps object in GlobalQuantityField (and the Quantity branch inside
ItemTableField) to move readOnly, min: 0 and the conditional max
(row.quantity_limit_per_sponsor) into htmlInput instead of input.

In `@src/components/mui/FormItemTable/components/ItemTableField.js`:
- Around line 46-53: Update the TextField's slotProps in ItemTableField.js so
the numeric constraints go to the native input element: move min
(field.minimum_quantity) and conditional max (field.maximum_quantity > 0 ?
field.maximum_quantity : undefined) from slotProps.input to slotProps.htmlInput
so the actual <input> receives these attributes; locate the slotProps object in
the ItemTableField component and replace the input key with htmlInput carrying
the min/max values.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-cart-form.js`:
- Line 30: The saveForm handler currently references an undefined variable
form_id which will throw at runtime; change the call in saveForm to pass the
correct identifier (use form.id) so it becomes saveForm = (values) =>
updateCartForm(form.id, values), ensuring you update the reference to the
defined prop/object used elsewhere in this component (verify the component
exports/props that provide form and that updateCartForm is the expected
function).
- Around line 16-19: Replace the non-existent imports updateCartForm and
getCartForm with the actual exported action creators getSponsorFormsForCart and
saveCartForm from sponsor-cart-actions, and update all uses in this component to
call saveCartForm instead of updateCartForm and getSponsorFormsForCart instead
of getCartForm; also fix the undefined variable by replacing form_id with
form.id wherever referenced (e.g., in event handlers or payload construction) so
the component uses the correct property names and action creators.
- Around line 32-34: The wrapper is passing the wrong props to EditForm and also
references form_id incorrectly; update the return to pass the props EditForm
expects: provide form={form} (fix any form_id -> form.id usage),
addOnId={addOnId}, cartFormId={cartFormId}, showMetadata={showMetadata},
showTimeZone={showTimeZone}, and rename saveForm to saveCartForm
(saveCartForm={saveForm}) so the prop names match EditForm’s signature; ensure
any internal references use form.id instead of form_id.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js`:
- Line 240: In handleSave, replace the undefined variable formId with the
correct identifier form.id when calling saveCartForm so the call becomes
saveCartForm(form.id, addOnId, items); update any references inside handleSave
that expect formId to use form.id instead; ensure this change is applied in the
edit-form component where handleSave is declared and where saveCartForm is
invoked.
- Around line 306-316: The file is using NotesModal and ItemSettingsModal in the
JSX but they are not imported; add import statements for NotesModal and
ItemSettingsModal at the top of the module (same module that defines the edit
form) so the components referenced in the JSX (NotesModal, ItemSettingsModal)
are resolved at runtime; ensure the import paths point to the correct files
(e.g., import NotesModal from "./NotesModal" and import ItemSettingsModal from
"./ItemSettingsModal" or the correct relative paths) and save.
- Line 24: The import for FormItemTable is pointing to the wrong directory;
update the import statement that references FormItemTable in the edit-form.js
module so it includes the mui folder (i.e., import the FormItemTable from the
components/mui/FormItemTable location) so the module resolves the component
correctly.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-page-template-dialog.test.js`:
- Around line 11-21: The test imports and mocks refer to the wrong component and
Redux pieces; update the test to import the actual component exported from the
tested module (use SelectFormDialog instead of SelectPageTemplateDialog from
"../index"), replace the mocked action module
"../../../actions/page-template-actions" with the correct actions module for the
form dialog (mock "../../../actions/sponsor-cart-actions" and the specific
action(s) used by SelectFormDialog), and adjust the mocked Redux state to the
slice shape SelectFormDialog expects (replace pageTemplateListState with the
correct state key and structure used by SelectFormDialog, e.g.,
sponsorCart/formTemplate list or whatever prop name SelectFormDialog reads).
Ensure the mocked action names and state keys match the component’s usage so the
test mounts the correct component with the expected props.

---

Nitpick comments:
In `@src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js`:
- Around line 35-72: The FormControl is marking errors via error={meta.touched
&& Boolean(meta.error)} but the component (MuiFormikDropdownCheckbox) never
renders the error text; add a FormHelperText under the Select that displays the
validation message when meta.touched && meta.error is truthy (e.g.,
<FormHelperText>{meta.touched && meta.error}</FormHelperText>), and ensure
FormHelperText is imported from `@mui/material` so the visual error text matches
the FormControl error state.

In `@src/components/mui/FormItemTable/index.js`:
- Around line 80-86: calculateTotal is recreated each render which invalidates
the totalAmount useMemo; wrap calculateTotal in React.useCallback so it is
stable between renders (keep its signature calculateTotal(row)), and include as
dependencies: currentApplicableRate, values, and calculateQuantity (and any
other helper it uses). Ensure you reference the same calculateTotal in the
totalAmount useMemo so the memo depends on the stable callback rather than a new
function each render.
- Around line 64-78: The calculateQuantity callback currently lists only
valuesStr as a dependency and reads extraColumns and values directly, which can
produce stale closures; update the useCallback dependencies to include
extraColumns and values (or a stable memoized reference to values) instead of
relying on valuesStr stringification, or memoize extraColumns where it’s created
and then include that memoized symbol; ensure the dependency array is [values,
extraColumns] (or [memoizedValues, memoizedExtraColumns]) so calculateQuantity
always sees the latest extraColumns and values.

In `@src/pages/sponsors/sponsor-cart-tab/components/cart-view.js`:
- Around line 35-37: The useEffect invoking getSponsorCart is missing
getSponsorCart in its dependency array; update the useEffect that calls
getSponsorCart to include getSponsorCart in the deps so React hooks linters are
satisfied, and if getSponsorCart is not a stable reference (e.g., coming from
props via mapDispatchToProps or recreated each render) make it stable by
memoizing it (move to a stable mapDispatchToProps, wrap it in useCallback where
defined, or otherwise ensure the reference is stable) before adding to the
dependency array.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js`:
- Around line 126-129: Replace uses of .map() that are used only for side
effects with .forEach() to avoid misleading code: change
item.meta_fields.map((f) => { ... }) to item.meta_fields.forEach((f) => { ... })
in the accumulator-building blocks (the snippet using
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = parseValue(f,
timeZone);) and apply the same change for the similar occurrence around the
other block (the code at the second occurrence that mirrors lines 142-145).

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`:
- Around line 173-177: SelectFormDialog.propTypes is missing several props used
by the component; add PropTypes entries for availableForms (array.isRequired or
array), sponsor (object.isRequired or shape), open (bool.isRequired), and
getSponsorFormsForCart (func.isRequired) to match how they're used. Update the
SelectFormDialog.propTypes declaration to include these keys with appropriate
PropTypes types (e.g., PropTypes.array, PropTypes.object or PropTypes.shape,
PropTypes.bool, PropTypes.func) and mark as required where the component depends
on them.
- Around line 38-40: The useEffect that calls getSponsorFormsForCart is missing
getSponsorFormsForCart in its dependency array; update the effect in
select-form-dialog's component so the dependency array includes
getSponsorFormsForCart (or alternatively ensure the prop is stable by wrapping
the action in useCallback where it's defined) to satisfy exhaustive-deps and
avoid stale closures—specifically modify the useEffect that currently calls
getSponsorFormsForCart() to depend on getSponsorFormsForCart.

In `@src/pages/sponsors/sponsor-cart-tab/index.js`:
- Around line 24-29: The component lacks a way to exit edit mode because
formEdit is never reset; add a handler (e.g., handleCloseEdit or
handleCancelEdit) that calls setFormEdit(null) and pass it into the EditForm as
an onCancel/onComplete prop so the EditForm can invoke it when the user cancels
or finishes editing; also update EditForm usage to call this callback instead of
relying on navigate(-1) (or call setFormEdit(null) alongside navigation) and
ensure handleAddForm still sets formEdit({ form, addOnId }) as before.

Comment on lines 226 to 261
export const saveCartForm = (formId, addOnId, items) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;

const params = {
access_token: accessToken
};

dispatch(startLoading());

const normalizedEntity = {
form_id: formId,
add_on_id: addOnId,
items: items.map((item) => ({
quantity: item.quantity,
add_on_item_id: item.id
}))
}

return postRequest(
null,
createAction(FORM_CART_SAVED),
`${window.API_BASE_URL}/api/v2/summits/${currentSummit.id}/sponsors`,
normalizedEntity,
snackbarErrorHandler
)(params)(dispatch).then(() => {
dispatch(stopLoading());
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("sponsor_list.sponsor_added")
})
);
});
}; No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how other cart actions construct their URLs and handle sponsorId
echo "=== Searching for PURCHASES_API_URL pattern in cart actions ==="
rg -n "PURCHASES_API_URL" src/actions/sponsor-cart-actions.js -A 3

echo ""
echo "=== Check for cart/forms endpoint patterns ==="
rg -n "carts.*forms\|forms.*cart" src/actions/ -B 2 -A 2

echo ""
echo "=== Check if sponsorId is retrieved in other functions ==="
rg -n "sponsorId" src/actions/sponsor-cart-actions.js -B 2 -A 2

echo ""
echo "=== Check what endpoints use /sponsors path ==="
rg -n "/summits/.*sponsors" src/actions/sponsor-cart-actions.js -B 3 -A 3

Repository: fntechgit/summit-admin

Length of output: 3688


🏁 Script executed:

#!/bin/bash
# Check translation keys for cart and sponsor operations
echo "=== Search for translation keys containing sponsor_added, cart_saved, form_saved ==="
rg -n "sponsor.*added\|cart.*saved\|form.*saved" src/i18n/ 

echo ""
echo "=== Check context around the success message translation ==="
rg -n "sponsor_list.sponsor_added" src/ -B 5 -A 5

Repository: fntechgit/summit-admin

Length of output: 1475


🏁 Script executed:

#!/bin/bash
# Get file size and examine the full sponsor-cart-actions.js to understand patterns
wc -l src/actions/sponsor-cart-actions.js

echo ""
echo "=== Check for other similar addOn or form-related functions ==="
rg -n "export const.*Form\|export const.*addOn" src/actions/sponsor-cart-actions.js -A 1

Repository: fntechgit/summit-admin

Length of output: 170


Fix incorrect API endpoint, retrieve sponsorId, and use appropriate success message.

The saveCartForm function posts to the wrong endpoint. It should follow the pattern of other cart actions in this file:

  • Use ${window.PURCHASES_API_URL} instead of ${window.API_BASE_URL}
  • Include the sponsorId (retrieved from currentSponsorState like other functions in this file)
  • Use the path /api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/carts/current/forms/${formId} instead of /api/v2/summits/${currentSummit.id}/sponsors
  • The success message translation key sponsor_list.sponsor_added is meant for sponsor creation operations, not cart form saves—use an appropriate translation key for this operation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 226 - 261, In saveCartForm,
the request is using the wrong base URL, missing sponsorId, and returns an
incorrect success message—retrieve sponsorId from currentSponsorState (similar
to other cart actions), switch the request base to window.PURCHASES_API_URL, and
post to the path
/api/v1/summits/${currentSummit.id}/sponsors/${sponsorId}/carts/current/forms/${formId}
(keep the normalizedEntity payload), then replace the snackbarSuccessHandler
translation key sponsor_list.sponsor_added with the correct cart-form success
key used elsewhere in this file; ensure the postRequest call still uses
createAction(FORM_CART_SAVED) and existing error handler.

Comment on lines +25 to +32
slotProps={{
input: {
readOnly: isReadOnly,
min: 0,
...(row.quantity_limit_per_sponsor
? { max: row.quantity_limit_per_sponsor }
: {})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's verify the file structure and locate the relevant files
ls -la src/components/mui/FormItemTable/components/ 2>/dev/null | head -20

Repository: fntechgit/summit-admin

Length of output: 315


🏁 Script executed:

#!/bin/bash
# Check the GlobalQuantityField component
cat src/components/mui/FormItemTable/components/GlobalQuantityField.js 2>/dev/null || echo "File not found"

Repository: fntechgit/summit-admin

Length of output: 1448


🏁 Script executed:

#!/bin/bash
# Check the MuiFormikTextField component
find . -name "*mui-formik-textfield*" -o -name "*MuiFormikTextField*" 2>/dev/null | head -10

Repository: fntechgit/summit-admin

Length of output: 124


🏁 Script executed:

#!/bin/bash
# Search for MUI TextField usages with slotProps patterns
rg "slotProps.*htmlInput|slotProps.*input" --type js -B 2 -A 2 | head -100

Repository: fntechgit/summit-admin

Length of output: 657


🏁 Script executed:

#!/bin/bash
# Check the MuiFormikTextField implementation
cat src/components/mui/formik-inputs/mui-formik-textfield.js

Repository: fntechgit/summit-admin

Length of output: 1350


🏁 Script executed:

#!/bin/bash
# Check ItemTableField to compare patterns
cat src/components/mui/FormItemTable/components/ItemTableField.js

Repository: fntechgit/summit-admin

Length of output: 2536


🏁 Script executed:

#!/bin/bash
# Search for htmlInput usage in the codebase
rg "slotProps.*htmlInput" --type js -B 2 -A 2 | head -150

Repository: fntechgit/summit-admin

Length of output: 48


Use slotProps.htmlInput instead of slotProps.input for min, max, and readOnly attributes.

min and max are native HTML input attributes that must reach the <input> element. Similarly, readOnly is typically a native input attribute. These should be passed via slotProps.htmlInput, not slotProps.input. The latter passes props to the Input component itself, which does not apply these HTML attributes to the native element.

Additionally, ItemTableField has the same issue in its Quantity case and should be corrected together.

Suggested fix for GlobalQuantityField:
slotProps={{
  htmlInput: {
    readOnly: isReadOnly,
    min: 0,
    ...(row.quantity_limit_per_sponsor
      ? { max: row.quantity_limit_per_sponsor }
      : {})
  }
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/components/GlobalQuantityField.js` around
lines 25 - 32, The slotProps currently use slotProps.input for native input
attributes in GlobalQuantityField (and the Quantity case in ItemTableField);
change those to slotProps.htmlInput so native attributes (readOnly, min, max)
are applied to the underlying <input> element—update the slotProps object in
GlobalQuantityField (and the Quantity branch inside ItemTableField) to move
readOnly, min: 0 and the conditional max (row.quantity_limit_per_sponsor) into
htmlInput instead of input.

Comment on lines +46 to +53
slotProps={{
input: {
min: field.minimum_quantity,
...(field.maximum_quantity > 0
? { max: field.maximum_quantity }
: {})
}
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

MUI TextField slotProps.htmlInput vs slotProps.input min max attributes

💡 Result:

In TextField, slotProps.input and slotProps.htmlInput target different layers:

  • slotProps.input → props for the MUI Input component (OutlinedInput / FilledInput / Input), i.e. the wrapper component around the native element. This is where you pass component-level props like startAdornment, endAdornment, sx, etc. (mui.com)
  • slotProps.htmlInput → attributes for the actual native <input> element (the “htmlInput” slot). This is where native constraints like min / max / step / maxLength / etc. belong. (mui.com)

So for min/max, use slotProps.htmlInput:

<TextField
  type="number"
  slotProps={{ htmlInput: { min: 0, max: 100 } }}
/>

This is the v6+ replacement for the deprecated inputProps (native <input> attrs) and InputProps (MUI Input component props):
inputPropsslotProps.htmlInput, and InputPropsslotProps.input. (mui.com)

Citations:


🏁 Script executed:

cat -n src/components/mui/FormItemTable/components/ItemTableField.js | sed -n '40,60p'

Repository: fntechgit/summit-admin

Length of output: 711


Move min/max to slotProps.htmlInput for the native input element to receive these constraints.

In MUI TextField, slotProps.input passes props to the MUI Input component wrapper, while slotProps.htmlInput passes attributes to the actual native <input> element. The min and max attributes must use slotProps.htmlInput to work:

Correct usage:
slotProps={{
  htmlInput: {
    min: field.minimum_quantity,
    ...(field.maximum_quantity > 0
      ? { max: field.maximum_quantity }
      : {})
  }
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/components/ItemTableField.js` around lines
46 - 53, Update the TextField's slotProps in ItemTableField.js so the numeric
constraints go to the native input element: move min (field.minimum_quantity)
and conditional max (field.maximum_quantity > 0 ? field.maximum_quantity :
undefined) from slotProps.input to slotProps.htmlInput so the actual <input>
receives these attributes; locate the slotProps object in the ItemTableField
component and replace the input key with htmlInput carrying the min/max values.

Comment on lines 16 to 19
import {
updateCartForm,
getCartForm
} from "../../../../../actions/sponsor-cart-actions";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check exported functions from sponsor-cart-actions
rg -n "^export\s+(const|function|async function)" src/actions/sponsor-cart-actions.js

Repository: fntechgit/summit-admin

Length of output: 965


🏁 Script executed:

#!/bin/bash
# Let me also check what the edit-cart-form.js file actually uses these imports for
sed -n '1,50p' src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-cart-form.js | cat -n

Repository: fntechgit/summit-admin

Length of output: 1539


🏁 Script executed:

#!/bin/bash
# Check if there are any other files trying to import these non-existent actions
rg "updateCartForm|getCartForm" src/ --type js

Repository: fntechgit/summit-admin

Length of output: 871


Replace non-existent imports with correct action creators.

updateCartForm and getCartForm do not exist in sponsor-cart-actions. The file exports getSponsorFormsForCart and saveCartForm instead. Update the imports at lines 16-19 and their usage in the component accordingly.

Additionally, line 30 references undefined variable form_id—should be form.id.

🧰 Tools
🪛 ESLint

[error] 17-17: updateCartForm not found in '../../../../../actions/sponsor-cart-actions'

(import/named)


[error] 18-18: getCartForm not found in '../../../../../actions/sponsor-cart-actions'

(import/named)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-cart-form.js`
around lines 16 - 19, Replace the non-existent imports updateCartForm and
getCartForm with the actual exported action creators getSponsorFormsForCart and
saveCartForm from sponsor-cart-actions, and update all uses in this component to
call saveCartForm instead of updateCartForm and getSponsorFormsForCart instead
of getCartForm; also fix the undefined variable by replacing form_id with
form.id wherever referenced (e.g., in event handlers or payload construction) so
the component uses the correct property names and action creators.


const getForm = () => getCartForm(form.id);

const saveForm = (values) => updateCartForm(form_id, values);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Undefined variable form_id will cause runtime error.

saveForm references form_id which is not defined anywhere in this component. This should likely be form.id.

Proposed fix
-  const saveForm = (values) => updateCartForm(form_id, values);
+  const saveForm = (values) => updateCartForm(form.id, values);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-cart-form.js`
at line 30, The saveForm handler currently references an undefined variable
form_id which will throw at runtime; change the call in saveForm to pass the
correct identifier (use form.id) so it becomes saveForm = (values) =>
updateCartForm(form.id, values), ensuring you update the reference to the
defined prop/object used elsewhere in this component (verify the component
exports/props that provide form and that updateCartForm is the expected
function).

Comment on lines 32 to 34
return (
<EditForm getForm={getForm} saveForm={saveForm} />
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's locate the EditForm component
find . -name "edit-form.js" -type f

Repository: fntechgit/summit-admin

Length of output: 199


🏁 Script executed:

#!/bin/bash
# Let's also check the EditCartForm file to see the full context
cat -n src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-cart-form.js

Repository: fntechgit/summit-admin

Length of output: 1539


🏁 Script executed:

#!/bin/bash
# Search for EditForm component definition to see what props it expects
rg "const EditForm|function EditForm" --max-count=5 -A 10

Repository: fntechgit/summit-admin

Length of output: 1495


🏁 Script executed:

#!/bin/bash
cat -n src/pages/sponsors/sponsor-cart-tab/components/edit-form.js

Repository: fntechgit/summit-admin

Length of output: 269


🏁 Script executed:

#!/bin/bash
# Also get more context from the actual EditForm implementation
cat -n src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js | head -50

Repository: fntechgit/summit-admin

Length of output: 2494


Fix props mismatch between EditCartForm and EditForm.

The EditForm component (in src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js) expects props: form, addOnId, cartFormId, showMetadata, showTimeZone, and saveCartForm. The wrapper passes getForm and saveForm functions which don't match the expected interface. Additionally, line 30 references undefined form_id instead of form.id.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-cart-form.js`
around lines 32 - 34, The wrapper is passing the wrong props to EditForm and
also references form_id incorrectly; update the return to pass the props
EditForm expects: provide form={form} (fix any form_id -> form.id usage),
addOnId={addOnId}, cartFormId={cartFormId}, showMetadata={showMetadata},
showTimeZone={showTimeZone}, and rename saveForm to saveCartForm
(saveCartForm={saveForm}) so the prop names match EditForm’s signature; ensure
any internal references use form.id instead of form_id.

import { useNavigate } from "react-router-dom";
import moment from "moment-timezone";
import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods";
import FormItemTable from "../../../../../components/FormItemTable";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find all FormItemTable files in the repository
fd -t f "FormItemTable" --extension js --extension jsx

Repository: fntechgit/summit-admin

Length of output: 130


🏁 Script executed:

# List all files in the FormItemTable directory
find src/components/mui/FormItemTable -type f

# Also check for the component file more broadly
fd -t f "FormItemTable" --extension js --extension jsx --extension ts --extension tsx

Repository: fntechgit/summit-admin

Length of output: 366


🏁 Script executed:

# Read the edit-form.js file to see the exact import
cat -n src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js | head -30

# Check if there's a FormItemTable at src/components/ (without mui)
find src/components -maxdepth 1 -name "FormItemTable*" -type f
find src/components -maxdepth 1 -name "FormItemTable*" -type d

Repository: fntechgit/summit-admin

Length of output: 1683


🏁 Script executed:

# Check all directories in src/components/ to see the actual structure
ls -la src/components/ | head -20

Repository: fntechgit/summit-admin

Length of output: 1242


🏁 Script executed:

# Continue listing src/components/
ls -la src/components/ | tail -20

Repository: fntechgit/summit-admin

Length of output: 1322


Correct the import path to include the mui directory.

The import path is missing mui. The FormItemTable component is located at src/components/mui/FormItemTable/index.js, so the import should be:

import FormItemTable from "../../../../../components/mui/FormItemTable";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js` at
line 24, The import for FormItemTable is pointing to the wrong directory; update
the import statement that references FormItemTable in the edit-form.js module so
it includes the mui folder (i.e., import the FormItemTable from the
components/mui/FormItemTable location) so the module resolves the component
correctly.

Comment on lines 306 to 316
<NotesModal
item={notesItem}
open={!!notesItem}
onClose={() => setNotesItem(null)}
onSave={formik.handleSubmit}
/>
<ItemSettingsModal
item={settingsItem}
open={!!settingsItem}
onClose={() => setSettingsItem(null)}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

NotesModal and ItemSettingsModal are not imported.

ESLint correctly flags that these components are used but never imported, which will cause a runtime error when the component renders.

Add the missing imports at the top of the file:

import NotesModal from "./NotesModal"; // Adjust path as needed
import ItemSettingsModal from "./ItemSettingsModal"; // Adjust path as needed
🧰 Tools
🪛 ESLint

[error] 306-306: 'NotesModal' is not defined.

(react/jsx-no-undef)


[error] 312-312: 'ItemSettingsModal' is not defined.

(react/jsx-no-undef)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/edit-form.js` around
lines 306 - 316, The file is using NotesModal and ItemSettingsModal in the JSX
but they are not imported; add import statements for NotesModal and
ItemSettingsModal at the top of the module (same module that defines the edit
form) so the components referenced in the JSX (NotesModal, ItemSettingsModal)
are resolved at runtime; ensure the import paths point to the correct files
(e.g., import NotesModal from "./NotesModal" and import ItemSettingsModal from
"./ItemSettingsModal" or the correct relative paths) and save.

Comment on lines 11 to 21
import SelectPageTemplateDialog from "../index";

jest.mock("i18n-react/dist/i18n-react", () => ({
__esModule: true,
default: { translate: (key) => key }
}));

// Mock Redux actions
jest.mock("../../../actions/page-template-actions", () => ({
getPageTemplates: jest.fn(() => () => Promise.resolve())
}));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify what component is exported from select-form-dialog/index.js
echo "=== Checking component export from index.js ==="
cat src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js | head -50

echo ""
echo "=== Checking if SelectPageTemplateDialog exists elsewhere ==="
find . -type f -name "*.js" -o -name "*.jsx" | xargs grep -l "SelectPageTemplateDialog" 2>/dev/null | head -20

echo ""
echo "=== Checking the action imports in the component ==="
rg -n "import.*from.*actions" src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/

Repository: fntechgit/summit-admin

Length of output: 2158


🏁 Script executed:

#!/bin/bash
# Check the full test file to see what's being imported and mocked
echo "=== Full test file content ==="
cat src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-page-template-dialog.test.js

echo ""
echo "=== Checking the correct test file name (if it exists) ==="
ls -la src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/ | grep -E "\.test\.|\.spec\."

Repository: fntechgit/summit-admin

Length of output: 11429


Test file imports and mocks the wrong component.

The test file imports SelectPageTemplateDialog from "../index", but the component exported at that path is SelectFormDialog. The test file also mocks the wrong Redux action (../../../actions/page-template-actions instead of sponsor-cart-actions) and uses the wrong Redux state slice (pageTemplateListState instead of the state structure expected by SelectFormDialog).

This test file appears to be copied from the SelectPageTemplateDialog tests without being properly adapted for SelectFormDialog. It will fail immediately because the component import doesn't exist at the mocked location.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-page-template-dialog.test.js`
around lines 11 - 21, The test imports and mocks refer to the wrong component
and Redux pieces; update the test to import the actual component exported from
the tested module (use SelectFormDialog instead of SelectPageTemplateDialog from
"../index"), replace the mocked action module
"../../../actions/page-template-actions" with the correct actions module for the
form dialog (mock "../../../actions/sponsor-cart-actions" and the specific
action(s) used by SelectFormDialog), and adjust the mocked Redux state to the
slice shape SelectFormDialog expects (replace pageTemplateListState with the
correct state key and structure used by SelectFormDialog, e.g.,
sponsorCart/formTemplate list or whatever prop name SelectFormDialog reads).
Ensure the mocked action names and state keys match the component’s usage so the
test mounts the correct component with the expected props.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (3)
src/components/mui/ItemSettingsModal/index.js (1)

72-76: Declare timeZone in PropTypes for clarity.

It’s used by ItemTableField, so documenting it avoids silent misuse.

🔧 Suggested fix
 ItemSettingsModal.propTypes = {
   item: PropTypes.object.isRequired,
+  timeZone: PropTypes.string,
   open: PropTypes.bool.isRequired,
   onClose: PropTypes.func.isRequired
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/ItemSettingsModal/index.js` around lines 72 - 76, Add the
missing timeZone PropType to the ItemSettingsModal component: update
ItemSettingsModal.propTypes to include timeZone (e.g.,
PropTypes.string.isRequired or PropTypes.string) since ItemTableField consumes
it; locate the ItemSettingsModal.propTypes block and add the timeZone entry with
the appropriate required/optional setting to match how ItemTableField expects
it.
src/components/mui/FormItemTable/index.js (2)

37-41: Guard against empty data array before accessing data[0].

While data[0]?.meta_fields?.filter handles undefined gracefully, other parts of the component (e.g., data.map on line 131) assume data is a non-empty array. Consider adding an early return or guard clause.

 const FormItemTable = ({
   data,
   rateDates,
   timeZone,
   values,
   onNotesClick,
   onSettingsClick
 }) => {
+  if (!data || data.length === 0) {
+    return null;
+  }
+
   const valuesStr = JSON.stringify(values);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 37 - 41, The
component assumes `data` is a non-empty array (e.g., computing
extraColumns/totalColumns and later using data.map in the component) which can
lead to runtime errors; add a guard at the top of the component to handle
empty/undefined `data` (return null/placeholder or an empty table) before
computing valuesStr/extraColumns/fixedColumns/totalColumns and before calling
data.map, or default `data` to an empty array and short-circuit rendering when
data.length === 0 to ensure the rest of the logic (including valuesStr,
extraColumns and the data.map usage) is safe.

86-92: calculateTotal accesses values directly but is not memoized.

This function is called during render (line 98 and 200). While it works correctly because it's inside the component, it would be more consistent to either memoize it or inline the logic where it's used.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 86 - 92,
calculateTotal currently closes over values and other props and is re-created on
every render; memoize it to avoid stale closures and unnecessary re-computation
by converting it into a useCallback (or replace with an inline useMemo at call
site) that depends on [values, calculateQuantity, currentApplicableRate]; ensure
the memoized function still accepts row and uses
values[`i-${row.form_item_id}-c-global-f-quantity`] || calculateQuantity(row)
and row.rates[currentApplicableRate], and update any usages (where
calculateTotal is called during render) to call the memoized calculateTotal to
keep behavior identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js`:
- Around line 13-17: Handle undefined options and ensure field.value is an array
in MuiFormikDropdownCheckbox: guard against options being undefined by
defaulting/using a safeOptions (e.g., const safeOptions = options || [])
wherever options is iterated (used for allSelected and maps), and normalize the
selected values coming from useField by coercing field.value to an array (e.g.,
const selected = Array.isArray(field.value) ? field.value : []) before using
.includes or other array methods; update references to options and field.value
(used in allSelected, onChange handlers, and any maps) to use safeOptions and
selected so rendering won’t crash when options are async or field.value is
undefined.

In `@src/components/mui/FormItemTable/components/ItemTableField.js`:
- Around line 1-11: Remove the unused imports T and METAFIELD_TYPES from
ItemTableField.js and reorder the import statements to satisfy ESLint import
ordering (external packages first: react and `@mui/material`, then third-party
packages like i18n-react if used, then local project imports such as
"../../formik-inputs/..." and "../../../../utils/constants"). Specifically,
delete the T and METAFIELD_TYPES import lines, ensure React and MenuItem imports
come before the local MuiFormik* imports, and keep only the imports referenced
by ItemTableField (e.g., MuiFormikCheckbox, MuiFormikDropdownCheckbox,
MuiFormikDropdownRadio, MuiFormikDatepicker, MuiFormikTimepicker,
MuiFormikTextField, MuiFormikSelect) so ESLint no longer reports unused imports
or ordering violations.

In `@src/components/mui/FormItemTable/index.js`:
- Around line 70-84: The calculateQuantity useCallback currently lists only
valuesStr as a dependency but reads extraColumns (and values), so update its
dependency array to include extraColumns (and values if values is mutable) so
the callback recomputes when extraColumns or values change; modify the
dependency array for calculateQuantity to include extraColumns and values (or
valuesStr and extraColumns together) to ensure correct updates.

In `@src/components/mui/NotesModal/index.js`:
- Around line 20-24: handleSave unconditionally calls onSave (and similarly the
handler at lines 64-68 calls onClear/onClose) but onSave/onClear aren't declared
in PropTypes; update the component to either (1) guard the calls by checking if
(onSave) onSave() and if (onClear) onClear() before invoking, and (2) add
PropTypes entries for onSave and onClear (or provide defaultProps with no-op
functions) so callers and linters know these props exist; locate the handlers
handleSave and the handler at lines ~64-68 and the component's PropTypes to make
these changes.
- Around line 1-24: NotesModal keeps a local notes state that isn't updated when
a different item is passed in, causing old notes to appear; update the notes
state whenever the item or the form field value changes by adding a useEffect in
NotesModal that reads field.value (or constructs name) and calls
setNotes(field?.value || "") when item?.form_item_id or field?.value changes, so
the local notes always reflect the current useField value before the modal is
shown; ensure the effect references the existing symbols name, useField (field),
setNotes, and item.

In `@src/components/mui/sponsor-addon-select.js`:
- Around line 15-25: The effect that calls querySponsorAddons should re-run when
sponsor or summitId change and guard against missing data: move the
sponsorshipIds computation into the useEffect (or include sponsorshipIds in the
dependency array) and update the useEffect dependency list to [sponsor?.id,
summitId, /*or sponsorshipIds*/]; ensure you check sponsor and
sponsor.sponsorships exist before calling querySponsorAddons, then call
querySponsorAddons(summitId, sponsor.id, sponsorshipIds, ...) and setOptions as
before so options refresh when props change.
- Around line 27-29: The handleChange handler currently uses ev.target.label
which is undefined for MUI Select; update handleChange (the function named
handleChange) to derive the selected option's name by finding the matching entry
in the options array (the same array used by renderValue) using ev.target.value,
then call onChange with { id: value, name: matchedOption?.label || '' } (or
similar fallback) so the name is populated correctly; ensure you reference the
same options variable used in renderValue when locating the label before
invoking onChange.

In `@src/pages/sponsors/edit-sponsor-page.js`:
- Line 266: The JSX in edit-sponsor-page.js passes an unused history prop to
SponsorCartTab; update the usage or the component: either remove history from
the <SponsorCartTab sponsor={entity} summitId={currentSummit.id}
history={history} /> call, or modify the SponsorCartTab component
(src/pages/sponsors/sponsor-cart-tab/index.js) to accept and use a history prop
(add history to the prop destructuring and implement navigation where needed).
Ensure props remain consistent between the caller (edit-sponsor-page.js) and the
component (SponsorCartTab).

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js`:
- Around line 150-186: The helper buildValidationSchema currently expects a full
form object but the caller passes form?.items, so the function returns an empty
schema; change buildValidationSchema to accept an items array (e.g., rename
parameter to items and use const items = itemsParam || []) and update its
internal references (keep getYupValidation, the acc keys like
i-{item.form_item_id}-c-{...}, and global quantity/notes handling) to operate on
that array; also update every call site (where buildValidationSchema is invoked)
to pass the items array directly (instead of a form) so the schema is
constructed correctly — likewise fix the other occurrence that mirrors this
logic (the similar block around the second build call).
- Around line 218-256: handleSave fails when values contains non-item keys like
discount/discount_type because the key.split("-") path produces invalid indices
and NaN ids; update handleSave to first detect and skip non-item keys (e.g.,
ensure the split produces the expected segments and that parseInt(match[1]) and
parseInt(match[5]) yield valid numbers) before proceeding, and guard lookups on
form.items and meta_fields (ensure metaField exists) before accessing
metaField.type; reference the handleSave reducer, resItem creation, the use of
match[], form.items.find(...).meta_fields.find(...), and the metaField.type
checks to apply these guards.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js`:
- Around line 30-32: The useEffect that calls getSponsorForm(formId) currently
has an empty dependency array so it won't re-run when the incoming formId prop
changes; update the effect in the component containing useEffect and
getSponsorForm to include formId in the dependency array (i.e., useEffect(() =>
{ getSponsorForm(formId); }, [formId])) so the form is refetched whenever formId
changes.

In `@src/pages/sponsors/sponsor-cart-tab/index.js`:
- Line 22: The initial state for formEdit is hardcoded to {formId: 19, addon:
null}, causing NewCartForm to render immediately; remove the debug value by
initializing formEdit to a falsy/empty state (e.g., null or {formId: null,
addon: null}) in the useState call and keep using setFormEdit to populate it
when a real edit is triggered so that the component conditionally renders
CartView until a real formEdit exists (refer to the formEdit and setFormEdit
state variables and the NewCartForm/CartView conditional rendering).

---

Duplicate comments:
In `@src/actions/sponsor-cart-actions.js`:
- Around line 248-283: The addCartForm function is using the wrong API base,
omitting the sponsorId, and showing the wrong success message; fix it by reading
currentSponsorState from getState to extract currentSponsor.id (use that as
sponsorId), switch the base URL to window.PURCHASES_API_URL instead of
window.API_BASE_URL, update the endpoint to include the sponsorId (e.g.,
.../api/v2/summits/${currentSummit.id}/sponsors/${sponsorId}/cart/forms), and
replace the snackbar success translation key (currently
"sponsor_list.sponsor_added") with an appropriate cart-form success key (e.g.,
"cart_form.added") so the message matches the operation.

In `@src/components/mui/FormItemTable/components/ItemTableField.js`:
- Around line 45-58: The numeric min/max attributes are being set on
slotProps.input (the MUI Input wrapper) so native constraints never reach the
actual <input>; update the MuiFormikTextField usage to move the min and max into
slotProps.htmlInput instead, using field.minimum_quantity for min and only set
max when field.maximum_quantity > 0 (same conditional), keeping the rest of
props (name, label, type="number", fullWidth, size="small") unchanged; locate
the MuiFormikTextField instance in ItemTableField.js and replace slotProps.input
with slotProps.htmlInput while preserving the conditional max logic.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js`:
- Around line 189-195: The EditForm component exposes props onSaveForm but never
uses it and references undefined variables saveCartForm, formId, and addOnId in
the save handler; update the component to wire handleSave to call the onSaveForm
prop (passing the validated form payload) and stop referencing undefined
identifiers—replace any use of saveCartForm with onSaveForm, ensure handleSave
reads the correct ids from the passed-in form prop (e.g., form.id and
form.addOnId or the prop names present) or from explicit props, and validate
those ids before calling onSaveForm so no undefined values are sent.

---

Nitpick comments:
In `@src/components/mui/FormItemTable/index.js`:
- Around line 37-41: The component assumes `data` is a non-empty array (e.g.,
computing extraColumns/totalColumns and later using data.map in the component)
which can lead to runtime errors; add a guard at the top of the component to
handle empty/undefined `data` (return null/placeholder or an empty table) before
computing valuesStr/extraColumns/fixedColumns/totalColumns and before calling
data.map, or default `data` to an empty array and short-circuit rendering when
data.length === 0 to ensure the rest of the logic (including valuesStr,
extraColumns and the data.map usage) is safe.
- Around line 86-92: calculateTotal currently closes over values and other props
and is re-created on every render; memoize it to avoid stale closures and
unnecessary re-computation by converting it into a useCallback (or replace with
an inline useMemo at call site) that depends on [values, calculateQuantity,
currentApplicableRate]; ensure the memoized function still accepts row and uses
values[`i-${row.form_item_id}-c-global-f-quantity`] || calculateQuantity(row)
and row.rates[currentApplicableRate], and update any usages (where
calculateTotal is called during render) to call the memoized calculateTotal to
keep behavior identical.

In `@src/components/mui/ItemSettingsModal/index.js`:
- Around line 72-76: Add the missing timeZone PropType to the ItemSettingsModal
component: update ItemSettingsModal.propTypes to include timeZone (e.g.,
PropTypes.string.isRequired or PropTypes.string) since ItemTableField consumes
it; locate the ItemSettingsModal.propTypes block and add the timeZone entry with
the appropriate required/optional setting to match how ItemTableField expects
it.

Comment on lines +13 to +17
const MuiFormikDropdownCheckbox = ({ name, options, ...rest }) => {
const [field, meta, helpers] = useField(name);
const allSelected = options.every(({ value }) =>
field.value?.includes(value)
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against undefined options and normalize selected values.

If options is undefined (e.g., async load), options.every(...) and options.map(...) will throw and crash the render. Defaulting options and normalizing field.value to an array keeps the component safe and controlled.

🛠️ Suggested fix
-const MuiFormikDropdownCheckbox = ({ name, options, ...rest }) => {
+const MuiFormikDropdownCheckbox = ({ name, options = [], ...rest }) => {
   const [field, meta, helpers] = useField(name);
-  const allSelected = options.every(({ value }) =>
-    field.value?.includes(value)
-  );
+  const selectedValues = Array.isArray(field.value) ? field.value : [];
+  const allSelected =
+    options.length > 0 &&
+    options.every(({ value }) => selectedValues.includes(value));
...
-        value={field.value || []}
+        value={selectedValues}
...
-          <MenuItem key={`ckbx-ddl-${value}`} value={value}>
-            <Checkbox checked={field.value?.includes(value)} />
+          <MenuItem key={`ckbx-ddl-${value}`} value={value}>
+            <Checkbox checked={selectedValues.includes(value)} />
             <ListItemText primary={label} />
          </MenuItem>

Also applies to: 40-67

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js` around
lines 13 - 17, Handle undefined options and ensure field.value is an array in
MuiFormikDropdownCheckbox: guard against options being undefined by
defaulting/using a safeOptions (e.g., const safeOptions = options || [])
wherever options is iterated (used for allSelected and maps), and normalize the
selected values coming from useField by coercing field.value to an array (e.g.,
const selected = Array.isArray(field.value) ? field.value : []) before using
.includes or other array methods; update references to options and field.value
(used in allSelected, onChange handlers, and any maps) to use safeOptions and
selected so rendering won’t crash when options are async or field.value is
undefined.

Comment on lines 1 to 11
import React from "react";
import MuiFormikCheckbox from "../../formik-inputs/mui-formik-checkbox";
import MuiFormikDropdownCheckbox from "../../formik-inputs/mui-formik-dropdown-checkbox";
import MuiFormikDropdownRadio from "../../formik-inputs/mui-formik-dropdown-radio";
import MuiFormikDatepicker from "../../formik-inputs/mui-formik-datepicker";
import MuiFormikTimepicker from "../../formik-inputs/mui-formik-timepicker";
import MuiFormikTextField from "../../formik-inputs/mui-formik-textfield";
import MuiFormikSelect from "../../formik-inputs/mui-formik-select";
import T from "i18n-react";
import { METAFIELD_TYPES } from "../../../../utils/constants";
import { MenuItem } from "@mui/material";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused imports and fix ESLint import order.

ESLint flags unused T/METAFIELD_TYPES and import ordering violations.

🔧 Suggested fix
-import React from "react";
-import MuiFormikCheckbox from "../../formik-inputs/mui-formik-checkbox";
-import MuiFormikDropdownCheckbox from "../../formik-inputs/mui-formik-dropdown-checkbox";
-import MuiFormikDropdownRadio from "../../formik-inputs/mui-formik-dropdown-radio";
-import MuiFormikDatepicker from "../../formik-inputs/mui-formik-datepicker";
-import MuiFormikTimepicker from "../../formik-inputs/mui-formik-timepicker";
-import MuiFormikTextField from "../../formik-inputs/mui-formik-textfield";
-import MuiFormikSelect from "../../formik-inputs/mui-formik-select";
-import T from "i18n-react";
-import { METAFIELD_TYPES } from "../../../../utils/constants";
-import { MenuItem } from "@mui/material";
+import React from "react";
+import { MenuItem } from "@mui/material";
+import MuiFormikCheckbox from "../../formik-inputs/mui-formik-checkbox";
+import MuiFormikDropdownCheckbox from "../../formik-inputs/mui-formik-dropdown-checkbox";
+import MuiFormikDropdownRadio from "../../formik-inputs/mui-formik-dropdown-radio";
+import MuiFormikDatepicker from "../../formik-inputs/mui-formik-datepicker";
+import MuiFormikTimepicker from "../../formik-inputs/mui-formik-timepicker";
+import MuiFormikTextField from "../../formik-inputs/mui-formik-textfield";
+import MuiFormikSelect from "../../formik-inputs/mui-formik-select";
🧰 Tools
🪛 ESLint

[error] 9-9: i18n-react import should occur before import of ../../formik-inputs/mui-formik-checkbox

(import/order)


[error] 9-9: 'T' is defined but never used.

(unused-imports/no-unused-imports)


[error] 9-9: 'T' is defined but never used.

(no-unused-vars)


[error] 10-10: 'METAFIELD_TYPES' is defined but never used.

(unused-imports/no-unused-imports)


[error] 10-10: 'METAFIELD_TYPES' is defined but never used.

(no-unused-vars)


[error] 11-11: @mui/material import should occur before import of ../../formik-inputs/mui-formik-checkbox

(import/order)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/components/ItemTableField.js` around lines 1
- 11, Remove the unused imports T and METAFIELD_TYPES from ItemTableField.js and
reorder the import statements to satisfy ESLint import ordering (external
packages first: react and `@mui/material`, then third-party packages like
i18n-react if used, then local project imports such as "../../formik-inputs/..."
and "../../../../utils/constants"). Specifically, delete the T and
METAFIELD_TYPES import lines, ensure React and MenuItem imports come before the
local MuiFormik* imports, and keep only the imports referenced by ItemTableField
(e.g., MuiFormikCheckbox, MuiFormikDropdownCheckbox, MuiFormikDropdownRadio,
MuiFormikDatepicker, MuiFormikTimepicker, MuiFormikTextField, MuiFormikSelect)
so ESLint no longer reports unused imports or ordering violations.

Comment on lines +70 to +84
const calculateQuantity = useCallback(
(row) => {
const qtyEXC = extraColumns.filter((exc) => exc.type === "Quantity");
return qtyEXC.reduce((res, exc) => {
const start = res > 0 ? res : 1;
return (
start *
(values?.[
`i-${row.form_item_id}-c-${exc.class_field}-f-${exc.type_id}`
] || 0)
);
}, 0);
},
[valuesStr]
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Dependency on valuesStr instead of extraColumns in calculateQuantity.

The calculateQuantity callback uses extraColumns but only lists valuesStr as a dependency. If extraColumns changes (e.g., different form data loaded), the callback won't update.

   const calculateQuantity = useCallback(
     (row) => {
       const qtyEXC = extraColumns.filter((exc) => exc.type === "Quantity");
       return qtyEXC.reduce((res, exc) => {
         const start = res > 0 ? res : 1;
         return (
           start *
           (values?.[
             `i-${row.form_item_id}-c-${exc.class_field}-f-${exc.type_id}`
           ] || 0)
         );
       }, 0);
     },
-    [valuesStr]
+    [valuesStr, extraColumns]
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const calculateQuantity = useCallback(
(row) => {
const qtyEXC = extraColumns.filter((exc) => exc.type === "Quantity");
return qtyEXC.reduce((res, exc) => {
const start = res > 0 ? res : 1;
return (
start *
(values?.[
`i-${row.form_item_id}-c-${exc.class_field}-f-${exc.type_id}`
] || 0)
);
}, 0);
},
[valuesStr]
);
const calculateQuantity = useCallback(
(row) => {
const qtyEXC = extraColumns.filter((exc) => exc.type === "Quantity");
return qtyEXC.reduce((res, exc) => {
const start = res > 0 ? res : 1;
return (
start *
(values?.[
`i-${row.form_item_id}-c-${exc.class_field}-f-${exc.type_id}`
] || 0)
);
}, 0);
},
[valuesStr, extraColumns]
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 70 - 84, The
calculateQuantity useCallback currently lists only valuesStr as a dependency but
reads extraColumns (and values), so update its dependency array to include
extraColumns (and values if values is mutable) so the callback recomputes when
extraColumns or values change; modify the dependency array for calculateQuantity
to include extraColumns and values (or valuesStr and extraColumns together) to
ensure correct updates.

Comment on lines +1 to +24
import React, { useState } from "react";
import T from "i18n-react/dist/i18n-react";
import PropTypes from "prop-types";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import { useField } from "formik";
import { Divider, IconButton, TextField } from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";

const NotesModal = ({ item, open, onClose, onSave }) => {
const name = `i-${item?.form_item_id}-c-global-f-notes`;
// eslint-disable-next-line
const [field, meta, helpers] = useField(name);
const [notes, setNotes] = useState(field?.value || "");

const handleSave = () => {
helpers.setValue(notes);
onClose();
onSave();
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Sync local notes state when the item changes.

As written, opening the modal for a different item can show the previous item’s notes.

🔧 Suggested fix
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
 ...
-  const [notes, setNotes] = useState(field?.value || "");
+  const [notes, setNotes] = useState(field?.value || "");
+
+  useEffect(() => {
+    setNotes(field?.value || "");
+  }, [field?.value, item?.form_item_id, open]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import React, { useState } from "react";
import T from "i18n-react/dist/i18n-react";
import PropTypes from "prop-types";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import { useField } from "formik";
import { Divider, IconButton, TextField } from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
const NotesModal = ({ item, open, onClose, onSave }) => {
const name = `i-${item?.form_item_id}-c-global-f-notes`;
// eslint-disable-next-line
const [field, meta, helpers] = useField(name);
const [notes, setNotes] = useState(field?.value || "");
const handleSave = () => {
helpers.setValue(notes);
onClose();
onSave();
};
import React, { useEffect, useState } from "react";
import T from "i18n-react/dist/i18n-react";
import PropTypes from "prop-types";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import { useField } from "formik";
import { Divider, IconButton, TextField } from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
const NotesModal = ({ item, open, onClose, onSave }) => {
const name = `i-${item?.form_item_id}-c-global-f-notes`;
// eslint-disable-next-line
const [field, meta, helpers] = useField(name);
const [notes, setNotes] = useState(field?.value || "");
useEffect(() => {
setNotes(field?.value || "");
}, [field?.value, item?.form_item_id, open]);
const handleSave = () => {
helpers.setValue(notes);
onClose();
onSave();
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/NotesModal/index.js` around lines 1 - 24, NotesModal keeps
a local notes state that isn't updated when a different item is passed in,
causing old notes to appear; update the notes state whenever the item or the
form field value changes by adding a useEffect in NotesModal that reads
field.value (or constructs name) and calls setNotes(field?.value || "") when
item?.form_item_id or field?.value changes, so the local notes always reflect
the current useField value before the modal is shown; ensure the effect
references the existing symbols name, useField (field), setNotes, and item.

Comment on lines +20 to +24
const handleSave = () => {
helpers.setValue(notes);
onClose();
onSave();
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard or require onSave before invoking.

onSave() is called unconditionally but isn’t defined in PropTypes.

🔧 Suggested fix
   const handleSave = () => {
     helpers.setValue(notes);
     onClose();
-    onSave();
+    if (onSave) onSave();
   };
 ...
 NotesModal.propTypes = {
   item: PropTypes.object.isRequired,
   open: PropTypes.bool.isRequired,
-  onClose: PropTypes.func.isRequired
+  onClose: PropTypes.func.isRequired,
+  onSave: PropTypes.func
 };

Also applies to: 64-68

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/NotesModal/index.js` around lines 20 - 24, handleSave
unconditionally calls onSave (and similarly the handler at lines 64-68 calls
onClear/onClose) but onSave/onClear aren't declared in PropTypes; update the
component to either (1) guard the calls by checking if (onSave) onSave() and if
(onClear) onClear() before invoking, and (2) add PropTypes entries for onSave
and onClear (or provide defaultProps with no-op functions) so callers and
linters know these props exist; locate the handlers handleSave and the handler
at lines ~64-68 and the component's PropTypes to make these changes.

</CustomTabPanel>
<CustomTabPanel value={selectedTab} index={5}>
<SponsorCartTab sponsor={entity} summitId={currentSummit.id} />
<SponsorCartTab sponsor={entity} summitId={currentSummit.id} history={history} />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused history prop passed to SponsorCartTab.

Based on the component definition in src/pages/sponsors/sponsor-cart-tab/index.js, SponsorCartTab only accepts sponsor and summitId props. The history prop is not destructured or used.

Either remove the unused prop or update SponsorCartTab to use it if navigation is needed.

-          <SponsorCartTab sponsor={entity} summitId={currentSummit.id} history={history} />
+          <SponsorCartTab sponsor={entity} summitId={currentSummit.id} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<SponsorCartTab sponsor={entity} summitId={currentSummit.id} history={history} />
<SponsorCartTab sponsor={entity} summitId={currentSummit.id} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/edit-sponsor-page.js` at line 266, The JSX in
edit-sponsor-page.js passes an unused history prop to SponsorCartTab; update the
usage or the component: either remove history from the <SponsorCartTab
sponsor={entity} summitId={currentSummit.id} history={history} /> call, or
modify the SponsorCartTab component
(src/pages/sponsors/sponsor-cart-tab/index.js) to accept and use a history prop
(add history to the prop destructuring and implement navigation where needed).
Ensure props remain consistent between the caller (edit-sponsor-page.js) and the
component (SponsorCartTab).

Comment on lines 150 to 186
const buildValidationSchema = (form) => {
const items = form.items || [];

const schema = items.reduce((acc, item) => {
item.meta_fields
.filter((f) => f.class_field === "Form")
.map((f) => {
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
getYupValidation(f);
});
// notes
acc[`i-${item.form_item_id}-c-global-f-notes`] = yup.string(
T.translate("validation.string")
);
// validation for the global quantity input
let globalQtySchema = yup
.number(T.translate("validation.number"))
.min(1, `${T.translate("validation.minimum")} 1`);
if (item.quantity_limit_per_sponsor > 0) {
globalQtySchema = globalQtySchema.max(
item.quantity_limit_per_sponsor,
T.translate("validation.maximum", {
maximum: item.quantity_limit_per_sponsor
})
);
}
globalQtySchema = globalQtySchema.required(
T.translate("validation.required")
);
acc[`i-${item.form_item_id}-c-global-f-quantity`] = globalQtySchema;
return acc;
}, {});

schema.discount = yup.number(T.translate("validation.number"))
schema.discount_type = yup.string(T.translate("validation.string")).nullable();

return schema;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validation schema isn’t built because buildValidationSchema receives the wrong argument.

You pass form?.items, but the helper expects a form object, so it returns an empty schema.

🔧 Suggested fix (accept items array)
-const buildValidationSchema = (form) => {
-  const items = form.items || [];
+const buildValidationSchema = (items = []) => {

Also applies to: 261-266

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 150 - 186, The helper buildValidationSchema currently expects a full form
object but the caller passes form?.items, so the function returns an empty
schema; change buildValidationSchema to accept an items array (e.g., rename
parameter to items and use const items = itemsParam || []) and update its
internal references (keep getYupValidation, the acc keys like
i-{item.form_item_id}-c-{...}, and global quantity/notes handling) to operate on
that array; also update every call site (where buildValidationSchema is invoked)
to pass the items array directly (instead of a form) so the schema is
constructed correctly — likewise fix the other occurrence that mirrors this
logic (the similar block around the second build call).

Comment on lines +30 to +32
useEffect(() => {
getSponsorForm(formId);
}, []);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check NewCartForm usage in parent components
rg -n --type=js -C5 'NewCartForm' src

Repository: fntechgit/summit-admin

Length of output: 3456


🏁 Script executed:

#!/bin/bash
# Find the parent component that uses NewCartForm
fd -e js sponsor-cart-tab | head -20

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# List the structure of the sponsor-cart-tab directory
find src/pages/sponsors/sponsor-cart-tab -type f -name "*.js" | head -30

Repository: fntechgit/summit-admin

Length of output: 497


🏁 Script executed:

#!/bin/bash
# View the complete new-cart-form.js file
cat -n src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js

Repository: fntechgit/summit-admin

Length of output: 1871


Add formId to the useEffect dependency array to refetch when the form changes.

When the parent component updates formId without remounting this component, the effect won't re-run and stale sponsor form data will be displayed. Add formId to the dependency array to ensure the form is fetched whenever it changes.

🔧 Suggested fix
  useEffect(() => {
-   getSponsorForm(formId);
- }, []);
+   if (formId) {
+     getSponsorForm(formId);
+   }
+ }, [formId, getSponsorForm]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js`
around lines 30 - 32, The useEffect that calls getSponsorForm(formId) currently
has an empty dependency array so it won't re-run when the incoming formId prop
changes; update the effect in the component containing useEffect and
getSponsorForm to include formId in the dependency array (i.e., useEffect(() =>
{ getSponsorForm(formId); }, [formId])) so the form is refetched whenever formId
changes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (3)
src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js (1)

31-33: ⚠️ Potential issue | 🟡 Minor

Refetch should depend on formId.

The effect runs only once. If formId changes on the same mounted instance, sponsor form data will be stale.

#!/bin/bash
# Check effect dependency and fetch call
rg -n "useEffect\\(|getSponsorForm\\(formId\\)|\\], \\[\\]" src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js -C3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js`
around lines 31 - 33, The current useEffect in new-cart-form.js calls
getSponsorForm(formId) only on mount, so updates to formId won't refetch; update
the dependency array to [formId] (or [formId, getSponsorForm] if getSponsorForm
isn't stable) so the effect reruns when formId changes, or alternatively memoize
getSponsorForm via useCallback before using it in the effect.
src/components/mui/FormItemTable/index.js (1)

75-89: ⚠️ Potential issue | 🟡 Minor

Include extraColumns in calculateQuantity dependencies.

calculateQuantity reads extraColumns (Line [77]) but the dependency array only tracks valuesStr (Line [88]), so callback logic can go stale when the selected form changes columns.

#!/bin/bash
# Verify current dependency list vs captured variables
rg -n "const calculateQuantity|extraColumns.filter|\\[valuesStr\\]" src/components/mui/FormItemTable/index.js -C3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 75 - 89, The
calculateQuantity useCallback currently closes over extraColumns but only lists
valuesStr in its dependency array, causing stale behavior; update the dependency
array for calculateQuantity to include extraColumns (e.g., [valuesStr,
extraColumns]) so the callback is recreated when extraColumns changes, ensuring
calculateQuantity (referenced by calculateQuantity and using
extraColumns.filter) always uses the current extraColumns.
src/actions/sponsor-cart-actions.js (1)

296-299: ⚠️ Potential issue | 🟡 Minor

Success toast key does not match this operation.

The success message uses sponsor_list.sponsor_added, which is unrelated to adding a cart form and will show confusing UX copy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 296 - 299, The success
toast currently calls snackbarSuccessHandler with the wrong translation key
("sponsor_list.sponsor_added"); update that call in
src/actions/sponsor-cart-actions.js (the snackbarSuccessHandler invocation) to
use the cart-form-specific key, e.g. replace "sponsor_list.sponsor_added" with
the appropriate cart form success key such as "sponsor_cart.cart_form_added" (or
the existing project i18n key that represents "cart form added") so the UX copy
matches the operation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/actions/sponsor-cart-actions.js`:
- Around line 279-284: The payload currently assigns formValues.discount_amount
(a dollar value) directly to normalizedEntity.discount_value, causing
inconsistency with cents-based math; update the code that builds
normalizedEntity (the normalizedEntity object in the sponsor cart action) to
convert formValues.discount_amount from dollars to integer cents (e.g., multiply
by 100 and round/parseInt) before assigning to discount_value so it matches
custom_rate/item normalization and totals calculation; ensure the conversion is
done where normalizedEntity is created so downstream code using discount_value
receives cents consistently.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js`:
- Around line 190-193: The Yup schema is validating "discount" but the form
field is named "discount_amount", so the discount input is not being validated;
update the schema object (the "schema" variable in edit-form/index.js) to
replace or add the field "discount_amount" (e.g., schema.discount_amount =
yup.number(T.translate("validation.number")) ) instead of schema.discount, and
keep discount_type validation as-is; also ensure any other nearby schema entries
(the block around lines referenced for discount) reference "discount_amount" so
Yup validates the actual form input.
- Around line 169-170: The validation message for the .min(1, ...) call is
passing a raw string with " 1" instead of interpolating the {minimum}
placeholder; update the .min call used in the validator (the line with .min(1,
`${T.translate("validation.minimum")} 1`) in the edit-form component) to call
the translation with interpolation (e.g., pass the minimum value as a parameter
to T.translate) so the {minimum} placeholder is replaced (keep the conditional
that checks item.quantity_limit_per_sponsor > 0 intact).
- Around line 131-134: Replace the uses of Array.prototype.map that are used
only for side effects with forEach: change the callback invocations where you
currently have item.meta_fields.map((f) => { ... }) (which sets
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = parseValue(f,
timeZone)) to item.meta_fields.forEach(...) and do the same replacement for the
second occurrence around lines 156-161 (the other map that mutates acc without
returning). This keeps parseValue and the acc key assignment logic intact but
uses the semantically correct forEach for side-effect-only iteration.

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`:
- Line 113: The Dialog currently calls the external onClose directly, which
bypasses local cleanup; update all Dialog elements (e.g., the Dialog at the
shown diff and the other Dialog around lines 54–58) to use the local handleClose
function instead of onClose, and implement handleClose to clear local state
(call setSelectedRows([]) and setSelectedAddon(null)) then call the external
onClose prop (if provided) so backdrop/Esc and other close paths always reset
selectedRows/selectedAddon before notifying the parent.
- Around line 68-70: The handleOnSave currently looks up form via forms.find(f
=> f.id === selectedRows[0]) and calls onSave(form, selectedAddon) without
checking that form exists; guard against a stale/filtered-out selection by
validating the lookup result in handleOnSave (or before calling it): if the
found form is undefined, abort (or show an error/toast) and do not call onSave,
or alternatively ensure the Save button is disabled when selectedRows[0] has no
matching form; update handleOnSave to check the found form and only call
onSave(form, selectedAddon) when form is truthy.

In `@src/pages/sponsors/sponsor-cart-tab/index.js`:
- Around line 35-49: NewCartForm expects formEdit to contain formId and addon
fields, but CartView currently passes setFormEdit directly
(onEdit={setFormEdit}) which provides a row with only id; update the onEdit
handler so it maps the row into the shape NewCartForm expects (populate formId
from row.id and provide addon as an object with id and name when present) before
calling setFormEdit, e.g., use onEdit={(row) => setFormEdit({ formId: row.id,
addon: row.addon ? { id: row.addon.id, name: row.addon.name } : null })} so
NewCartForm.formId and NewCartForm.addOn access work correctly.

---

Duplicate comments:
In `@src/actions/sponsor-cart-actions.js`:
- Around line 296-299: The success toast currently calls snackbarSuccessHandler
with the wrong translation key ("sponsor_list.sponsor_added"); update that call
in src/actions/sponsor-cart-actions.js (the snackbarSuccessHandler invocation)
to use the cart-form-specific key, e.g. replace "sponsor_list.sponsor_added"
with the appropriate cart form success key such as
"sponsor_cart.cart_form_added" (or the existing project i18n key that represents
"cart form added") so the UX copy matches the operation.

In `@src/components/mui/FormItemTable/index.js`:
- Around line 75-89: The calculateQuantity useCallback currently closes over
extraColumns but only lists valuesStr in its dependency array, causing stale
behavior; update the dependency array for calculateQuantity to include
extraColumns (e.g., [valuesStr, extraColumns]) so the callback is recreated when
extraColumns changes, ensuring calculateQuantity (referenced by
calculateQuantity and using extraColumns.filter) always uses the current
extraColumns.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js`:
- Around line 31-33: The current useEffect in new-cart-form.js calls
getSponsorForm(formId) only on mount, so updates to formId won't refetch; update
the dependency array to [formId] (or [formId, getSponsorForm] if getSponsorForm
isn't stable) so the effect reruns when formId changes, or alternatively memoize
getSponsorForm via useCallback before using it in the effect.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6393359 and 969fcab.

📒 Files selected for processing (10)
  • src/actions/sponsor-cart-actions.js
  • src/components/mui/FormItemTable/index.js
  • src/i18n/en.json
  • src/pages/sponsors/sponsor-cart-tab/components/cart-view.js
  • src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js
  • src/pages/sponsors/sponsor-cart-tab/components/edit-form/new-cart-form.js
  • src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js
  • src/pages/sponsors/sponsor-cart-tab/index.js
  • src/reducers/sponsors/sponsor-page-cart-list-reducer.js
  • src/utils/constants.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/utils/constants.js
  • src/pages/sponsors/sponsor-cart-tab/components/cart-view.js

Comment on lines 279 to 284
const normalizedEntity = {
form_id: formId,
addon_id: addOnId,
discount_type: formValues.discount_type,
discount_value: formValues.discount_amount,
items: normalizeItems(formValues.items)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Normalize amount discounts before sending discount_value.

discount_amount comes from a dollar input, but payload sends it raw. This is inconsistent with the cents-based math used for totals and custom_rate normalization.

💡 Suggested fix
+import { FORM_DISCOUNT_OPTIONS } from "../utils/constants";
@@
     const normalizedEntity = {
       form_id: formId,
       addon_id: addOnId,
       discount_type: formValues.discount_type,
-      discount_value: formValues.discount_amount,
+      discount_value:
+        formValues.discount_type === FORM_DISCOUNT_OPTIONS.AMOUNT
+          ? amountToCents(formValues.discount_amount || 0)
+          : formValues.discount_amount,
       items: normalizeItems(formValues.items)
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 279 - 284, The payload
currently assigns formValues.discount_amount (a dollar value) directly to
normalizedEntity.discount_value, causing inconsistency with cents-based math;
update the code that builds normalizedEntity (the normalizedEntity object in the
sponsor cart action) to convert formValues.discount_amount from dollars to
integer cents (e.g., multiply by 100 and round/parseInt) before assigning to
discount_value so it matches custom_rate/item normalization and totals
calculation; ensure the conversion is done where normalizedEntity is created so
downstream code using discount_value receives cents consistently.

Comment on lines +131 to +134
item.meta_fields.map((f) => {
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
parseValue(f, timeZone);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js | sed -n '125,170p'

Repository: fntechgit/summit-admin

Length of output: 1894


Use forEach instead of map for side effects (Biome error).

Lines 131-134 and 156-161 both use map() callbacks that have no return statements and only mutate acc. Since the array results are discarded, forEach() is the semantically correct method and will resolve the Biome linting error.

Suggested fix
-    item.meta_fields.map((f) => {
+    item.meta_fields.forEach((f) => {
       acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
         parseValue(f, timeZone);
     });
@@
-    item.meta_fields
-      .filter((f) => f.class_field === "Form")
-      .map((f) => {
+    item.meta_fields
+      .filter((f) => f.class_field === "Form")
+      .forEach((f) => {
         acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
           getYupValidation(f);
       });
🧰 Tools
🪛 Biome (2.4.4)

[error] 131-131: This callback passed to map() iterable method should always return a value.

(lint/suspicious/useIterableCallbackReturn)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 131 - 134, Replace the uses of Array.prototype.map that are used only for
side effects with forEach: change the callback invocations where you currently
have item.meta_fields.map((f) => { ... }) (which sets
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = parseValue(f,
timeZone)) to item.meta_fields.forEach(...) and do the same replacement for the
second occurrence around lines 156-161 (the other map that mutates acc without
returning). This keeps parseValue and the acc key assignment logic intact but
uses the semantically correct forEach for side-effect-only iteration.

Comment on lines +169 to +170
.min(1, `${T.translate("validation.minimum")} 1`);
if (item.quantity_limit_per_sponsor > 0) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minimum validation message interpolation is malformed.

"${T.translate("validation.minimum")} 1" leaves the {minimum} placeholder unresolved.

💡 Suggested fix
-      .min(1, `${T.translate("validation.minimum")} 1`);
+      .min(1, T.translate("validation.minimum", { minimum: 1 }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.min(1, `${T.translate("validation.minimum")} 1`);
if (item.quantity_limit_per_sponsor > 0) {
.min(1, T.translate("validation.minimum", { minimum: 1 }));
if (item.quantity_limit_per_sponsor > 0) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 169 - 170, The validation message for the .min(1, ...) call is passing a
raw string with " 1" instead of interpolating the {minimum} placeholder; update
the .min call used in the validator (the line with .min(1,
`${T.translate("validation.minimum")} 1`) in the edit-form component) to call
the translation with interpolation (e.g., pass the minimum value as a parameter
to T.translate) so the {minimum} placeholder is replaced (keep the conditional
that checks item.quantity_limit_per_sponsor > 0 intact).

Comment on lines +190 to +193
schema.discount = yup.number(T.translate("validation.number"));
schema.discount_type = yup
.string(T.translate("validation.string"))
.nullable();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Discount validation is bound to the wrong field name.

Schema defines discount, but the form input uses discount_amount (Line [288]). Current discount input is not actually validated by Yup.

💡 Suggested fix
-  schema.discount = yup.number(T.translate("validation.number"));
+  schema.discount_amount = yup
+    .number(T.translate("validation.number"))
+    .min(0, T.translate("validation.minimum", { minimum: 0 }));

Also applies to: 287-289

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 190 - 193, The Yup schema is validating "discount" but the form field is
named "discount_amount", so the discount input is not being validated; update
the schema object (the "schema" variable in edit-form/index.js) to replace or
add the field "discount_amount" (e.g., schema.discount_amount =
yup.number(T.translate("validation.number")) ) instead of schema.discount, and
keep discount_type validation as-is; also ensure any other nearby schema entries
(the block around lines referenced for discount) reference "discount_amount" so
Yup validates the actual form input.

Comment on lines +68 to +70
const handleOnSave = () => {
const form = forms.find((f) => f.id === selectedRows[0]);
onSave(form, selectedAddon);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard handleOnSave against stale/filtered-out selection.

After filtering/sorting, selectedRows[0] may not exist in forms; onSave(form, ...) can pass undefined and break downstream code expecting form.id.

💡 Suggested fix
   const handleOnSave = () => {
     const form = forms.find((f) => f.id === selectedRows[0]);
+    if (!form) return;
     onSave(form, selectedAddon);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleOnSave = () => {
const form = forms.find((f) => f.id === selectedRows[0]);
onSave(form, selectedAddon);
const handleOnSave = () => {
const form = forms.find((f) => f.id === selectedRows[0]);
if (!form) return;
onSave(form, selectedAddon);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`
around lines 68 - 70, The handleOnSave currently looks up form via forms.find(f
=> f.id === selectedRows[0]) and calls onSave(form, selectedAddon) without
checking that form exists; guard against a stale/filtered-out selection by
validating the lookup result in handleOnSave (or before calling it): if the
found form is undefined, abort (or show an error/toast) and do not call onSave,
or alternatively ensure the Save button is disabled when selectedRows[0] has no
matching form; update handleOnSave to check the found form and only call
onSave(form, selectedAddon) when form is truthy.

];

return (
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use handleClose in Dialog.onClose to reset local state consistently.

Backdrop/Esc currently calls onClose directly and skips clearing selectedRows/selectedAddon.

💡 Suggested fix
-    <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
+    <Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>

Also applies to: 54-58

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js`
at line 113, The Dialog currently calls the external onClose directly, which
bypasses local cleanup; update all Dialog elements (e.g., the Dialog at the
shown diff and the other Dialog around lines 54–58) to use the local handleClose
function instead of onClose, and implement handleClose to clear local state
(call setSelectedRows([]) and setSelectedAddon(null)) then call the external
onClose prop (if provided) so backdrop/Esc and other close paths always reset
selectedRows/selectedAddon before notifying the parent.

Comment on lines +35 to +49
{formEdit && (
<NewCartForm
formId={formEdit.formId}
addOn={{
addon_name: formEdit.addon?.name,
addon_id: formEdit.addon?.id
}}
onCancel={() => setFormEdit(null)}
onSaveCallback={handleOnFormAdded}
/>
)}
{!!cart && (
<Paper elevation={0} sx={{ width: "100%", mb: 2 }}>
<MuiTable
columns={tableColumns}
data={cart?.forms}
options={{}}
onEdit={handleEdit}
onDelete={handleDelete}
>
<TotalRow
columns={tableColumns}
total={cart?.total}
targetCol="amount"
trailing={2}
/>
</MuiTable>
<Box
sx={{
display: "flex",
justifyContent: "flex-end",
mt: 4,
pb: 4,
gap: "10px"
}}
>
<Button
variant="contained"
color="primary"
style={{ minWidth: 250 }}
onClick={handlePayCreditCard}
>
{T.translate("edit_sponsor.cart_tab.pay_cc")}
</Button>
<Button
variant="contained"
color="primary"
style={{ minWidth: 250 }}
onClick={handlePayInvoice}
>
{T.translate("edit_sponsor.cart_tab.pay_invoice")}
</Button>
</Box>
</Paper>
{!formEdit && (
<CartView
onEdit={setFormEdit}
onAddForm={() => setOpenAddFormDialog(true)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

onEdit is passing an incompatible shape into formEdit.

NewCartForm reads formEdit.formId (Line [37]), but CartView edit callback is wired as onEdit={setFormEdit} (Line [48]). If the row object only has id, this results in formId being undefined and broken fetch/save flow.

💡 Suggested fix
+  const handleEditForm = (row) => {
+    setFormEdit({
+      formId: row.form_id ?? row.id,
+      addon:
+        row.addon_id != null
+          ? { id: row.addon_id, name: row.addon_name }
+          : null
+    });
+  };
+
   {!formEdit && (
     <CartView
-      onEdit={setFormEdit}
+      onEdit={handleEditForm}
       onAddForm={() => setOpenAddFormDialog(true)}
     />
   )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{formEdit && (
<NewCartForm
formId={formEdit.formId}
addOn={{
addon_name: formEdit.addon?.name,
addon_id: formEdit.addon?.id
}}
onCancel={() => setFormEdit(null)}
onSaveCallback={handleOnFormAdded}
/>
)}
{!!cart && (
<Paper elevation={0} sx={{ width: "100%", mb: 2 }}>
<MuiTable
columns={tableColumns}
data={cart?.forms}
options={{}}
onEdit={handleEdit}
onDelete={handleDelete}
>
<TotalRow
columns={tableColumns}
total={cart?.total}
targetCol="amount"
trailing={2}
/>
</MuiTable>
<Box
sx={{
display: "flex",
justifyContent: "flex-end",
mt: 4,
pb: 4,
gap: "10px"
}}
>
<Button
variant="contained"
color="primary"
style={{ minWidth: 250 }}
onClick={handlePayCreditCard}
>
{T.translate("edit_sponsor.cart_tab.pay_cc")}
</Button>
<Button
variant="contained"
color="primary"
style={{ minWidth: 250 }}
onClick={handlePayInvoice}
>
{T.translate("edit_sponsor.cart_tab.pay_invoice")}
</Button>
</Box>
</Paper>
{!formEdit && (
<CartView
onEdit={setFormEdit}
onAddForm={() => setOpenAddFormDialog(true)}
const handleEditForm = (row) => {
setFormEdit({
formId: row.form_id ?? row.id,
addon:
row.addon_id != null
? { id: row.addon_id, name: row.addon_name }
: null
});
};
{formEdit && (
<NewCartForm
formId={formEdit.formId}
addOn={{
addon_name: formEdit.addon?.name,
addon_id: formEdit.addon?.id
}}
onCancel={() => setFormEdit(null)}
onSaveCallback={handleOnFormAdded}
/>
)}
{!formEdit && (
<CartView
onEdit={handleEditForm}
onAddForm={() => setOpenAddFormDialog(true)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/index.js` around lines 35 - 49,
NewCartForm expects formEdit to contain formId and addon fields, but CartView
currently passes setFormEdit directly (onEdit={setFormEdit}) which provides a
row with only id; update the onEdit handler so it maps the row into the shape
NewCartForm expects (populate formId from row.id and provide addon as an object
with id and name when present) before calling setFormEdit, e.g., use
onEdit={(row) => setFormEdit({ formId: row.id, addon: row.addon ? { id:
row.addon.id, name: row.addon.name } : null })} so NewCartForm.formId and
NewCartForm.addOn access work correctly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js (3)

482-490: Test doesn't actually verify the "load more" guard condition.

This test only verifies getSponsorFormsForCart was called once (the initial load), but it doesn't click the "Load More" button to verify that clicking it when total === forms.length results in no additional call. The button still exists in the DOM—the test should click it and verify no extra call is made.

📝 Suggested improvement to test the guard condition
   test("does not load more when all items are displayed", async () => {
     const {
       getSponsorFormsForCart
     } = require("../../../../../../actions/sponsor-cart-actions");
     renderWithStore({}, { total: mockForms.length });

-    // Initial call only
-    expect(getSponsorFormsForCart).toHaveBeenCalledTimes(1);
+    // Initial call
+    expect(getSponsorFormsForCart).toHaveBeenCalledTimes(1);
+    
+    // Click load more - should not trigger additional call
+    const loadMoreButton = screen.getByTestId("load-more");
+    await userEvent.click(loadMoreButton);
+    
+    // Still only the initial call
+    expect(getSponsorFormsForCart).toHaveBeenCalledTimes(1);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`
around lines 482 - 490, The test "does not load more when all items are
displayed" currently only asserts getSponsorFormsForCart was called once; update
it to actually click the "Load More" control and assert no additional calls
occur: after renderWithStore({}, { total: mockForms.length }) find the Load More
button (by text "Load More" or the component's visible label) and simulate a
click using fireEvent.click or userEvent.click, then assert
getSponsorFormsForCart was still called only once; reference
getSponsorFormsForCart, renderWithStore, and mockForms to locate the test and
the button in the component.

159-170: Parameter naming is misleading.

The storeState parameter is spread inside availableForms, not at the store root level. This could confuse maintainers expecting to override other parts of the store state.

📝 Suggested rename for clarity
-const renderWithStore = (props, storeState = {}) => {
+const renderWithStore = (props, availableFormsOverrides = {}) => {
   const defaultState = {
     sponsorPageCartListState: {
       availableForms: {
         forms: mockForms,
         currentPage: 1,
         term: "",
         order: "id",
         orderDir: 1,
         total: mockForms.length,
-        ...storeState
+        ...availableFormsOverrides
       }
     }
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`
around lines 159 - 170, The parameter name storeState in the renderWithStore
helper is misleading because it is merged into
sponsorPageCartListState.availableForms, not the root store; rename it (e.g.,
availableFormsOverride) and update renderWithStore's implementation to spread
availableFormsOverride into availableForms, and update all test calls to pass
overrides using the new name so intent is clear (refer to renderWithStore and
sponsorPageCartListState.availableForms to locate the change).

384-401: Consider verifying complete reset state after save.

The test verifies the radio button is unchecked but doesn't assert that the selection count returns to "0 items selected" or that the addon selection is cleared.

📝 Add assertions for complete reset verification
     await userEvent.click(saveButton);

     // Selection should be reset
     expect(radioButtons[0]).not.toBeChecked();
+    expect(screen.getByText("0 items selected")).toBeInTheDocument();
+    expect(screen.getByTestId("addon-select")).toHaveValue("");
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`
around lines 384 - 401, In the "resets selection after save" test, after
clicking the save button (saveButton) add assertions to verify the full reset:
assert that the selection summary text updates to "0 items selected" (use
screen.getByText or queryByText) and that all radioButtons returned by
screen.getAllByRole("radio") are not checked, and if there is an addon selection
element in this dialog assert it is cleared as well; keep using renderWithStore,
onSave and userEvent as in the test to reproduce the save flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js`:
- Around line 649-664: The test "shows $0.00 when no quantities are set"
currently only checks for the TOTAL label; update the test in
FormItemTable.test.js to also assert the displayed total amount equals "$0.00"
by querying the rendered output for the value (e.g., using
screen.getByText(/\$0\.00/) or screen.getByRole if the total is in a semantic
element) after rendering <FormItemTableWrapper data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES} timeZone="America/New_York" initialValues={{}} ...
/> so the behavior of FormItemTableWrapper's total calculation is actually
verified.
- Around line 571-576: The test fixture in the meta_fields array uses the
property class: "Form" which doesn't match the component's contract (it expects
field.class_field); update the fixture object in FormItemTable.test.js so the
property is class_field: "Form" (and remove or rename the existing class key) so
the test exercises the same shape the FormItemTable component uses.
- Around line 686-715: The tests "highlights standard rate when current time is
between early_bird and standard" and "highlights onsite rate when current time
is after standard_rate" are using non-existent MOCK_RATE_DATES keys
(early_bird_rate, standard_rate) which causes Date.now() to produce NaN; update
the jest.spyOn(Date, "now").mockImplementation calls to reference the correct
keys on MOCK_RATE_DATES (e.g., MOCK_RATE_DATES.early_bird and
MOCK_RATE_DATES.standard or whatever exact key names exist on MOCK_RATE_DATES)
so the mocked timestamps compute valid numbers and the rate-window behavior in
the FormItemTable tests is actually exercised.
- Around line 668-727: The tests are false positives because they only assert
that the rate text exists; instead, locate the DOM node for the specific rate
rendered by FormItemTableWrapper (use getByText/getAllByText for
"$150.00"/"$188.00"/"$224.00" then navigate to its cell/parent) and assert that
that cell has the component's highlight indicator (the CSS class or data
attribute that marks the applicable rate in FormItemTable, e.g., a class like
"applicable" / "applicable-rate" or a data attribute like "data-applicable");
update the three specs that mock Date.now (using MOCK_TIME_BEFORE_EARLY_BIRD,
MOCK_RATE_DATES with TIME_OFFSET and MILLISECONDS_MULTIPLIER) to assert the
presence of that highlight marker on the expected rate cell rather than just the
text existing.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`:
- Around line 415-432: The test "resets selected rows and addon when closing"
currently only asserts onClose was called; after invoking
userEvent.click(closeButton) update the assertions to verify the UI/state reset:
assert the selection count is back to "0 items selected" (e.g.,
expect(screen.getByText("0 items selected")).toBeInTheDocument()) and assert the
addon select is cleared (e.g., expect(addonSelect).toHaveValue("") or query the
option selection). If the dialog unmounts on close, re-open or re-render via
renderWithStore (or call whatever opens the dialog) and then assert radioButtons
selection is cleared and addonSelect has no value. Keep references to onClose,
renderWithStore, radioButtons, addonSelect and closeButton to locate the code to
change.
- Around line 233-238: The close IconButton in the SelectFormDialog component is
missing an accessible label, causing the test in select-form-dialog.test.js to
query with an empty name; add aria-label="close" to the IconButton element in
the SelectFormDialog (component where IconButton is rendered) and then update
the test to select the button with screen.getByRole("button", { name: /close/i
}) (the test uses renderWithStore and assertions on screen.getByRole).

---

Nitpick comments:
In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`:
- Around line 482-490: The test "does not load more when all items are
displayed" currently only asserts getSponsorFormsForCart was called once; update
it to actually click the "Load More" control and assert no additional calls
occur: after renderWithStore({}, { total: mockForms.length }) find the Load More
button (by text "Load More" or the component's visible label) and simulate a
click using fireEvent.click or userEvent.click, then assert
getSponsorFormsForCart was still called only once; reference
getSponsorFormsForCart, renderWithStore, and mockForms to locate the test and
the button in the component.
- Around line 159-170: The parameter name storeState in the renderWithStore
helper is misleading because it is merged into
sponsorPageCartListState.availableForms, not the root store; rename it (e.g.,
availableFormsOverride) and update renderWithStore's implementation to spread
availableFormsOverride into availableForms, and update all test calls to pass
overrides using the new name so intent is clear (refer to renderWithStore and
sponsorPageCartListState.availableForms to locate the change).
- Around line 384-401: In the "resets selection after save" test, after clicking
the save button (saveButton) add assertions to verify the full reset: assert
that the selection summary text updates to "0 items selected" (use
screen.getByText or queryByText) and that all radioButtons returned by
screen.getAllByRole("radio") are not checked, and if there is an addon selection
element in this dialog assert it is cleared as well; keep using renderWithStore,
onSave and userEvent as in the test to reproduce the save flow.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 969fcab and 2573190.

📒 Files selected for processing (2)
  • src/components/mui/FormItemTable/__tests__/FormItemTable.test.js
  • src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js

Comment on lines +571 to +576
meta_fields: [
{
type_id: 100,
class: "Form",
name: "Description",
type: "Text"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fixture shape is inconsistent with component contract.

Line 574 uses class: "Form" while the component logic uses field.class_field. This test data can bypass realistic behavior and hide issues.

🔧 Proposed fix
           meta_fields: [
             {
               type_id: 100,
-              class: "Form",
+              class_field: "Form",
               name: "Description",
               type: "Text"
             }
           ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
meta_fields: [
{
type_id: 100,
class: "Form",
name: "Description",
type: "Text"
meta_fields: [
{
type_id: 100,
class_field: "Form",
name: "Description",
type: "Text"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 571 - 576, The test fixture in the meta_fields array uses the property
class: "Form" which doesn't match the component's contract (it expects
field.class_field); update the fixture object in FormItemTable.test.js so the
property is class_field: "Form" (and remove or rename the existing class key) so
the test exercises the same shape the FormItemTable component uses.

Comment on lines +649 to +664
it("shows $0.00 when no quantities are set", () => {
render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
initialValues={{}}
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);

expect(
screen.getByText("edit_sponsor.cart_tab.edit_form.total_on_caps")
).toBeInTheDocument();
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

The $0.00 test does not assert $0.00.

Line 649’s test name says it validates zero total, but it only checks the TOTAL label. Add a value assertion so behavior is actually verified.

🔧 Proposed fix
     it("shows $0.00 when no quantities are set", () => {
       render(
         <FormItemTableWrapper
           data={MOCK_FORM_A.items}
           rateDates={MOCK_RATE_DATES}
           timeZone="America/New_York"
           initialValues={{}}
           onNotesClick={mockOnNotesClick}
           onSettingsClick={mockOnSettingsClick}
         />
       );

       expect(
         screen.getByText("edit_sponsor.cart_tab.edit_form.total_on_caps")
       ).toBeInTheDocument();
+      expect(screen.getByText("$0.00")).toBeInTheDocument();
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("shows $0.00 when no quantities are set", () => {
render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
initialValues={{}}
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);
expect(
screen.getByText("edit_sponsor.cart_tab.edit_form.total_on_caps")
).toBeInTheDocument();
});
it("shows $0.00 when no quantities are set", () => {
render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
initialValues={{}}
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);
expect(
screen.getByText("edit_sponsor.cart_tab.edit_form.total_on_caps")
).toBeInTheDocument();
expect(screen.getByText("$0.00")).toBeInTheDocument();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 649 - 664, The test "shows $0.00 when no quantities are set" currently
only checks for the TOTAL label; update the test in FormItemTable.test.js to
also assert the displayed total amount equals "$0.00" by querying the rendered
output for the value (e.g., using screen.getByText(/\$0\.00/) or
screen.getByRole if the total is in a semantic element) after rendering
<FormItemTableWrapper data={MOCK_FORM_A.items} rateDates={MOCK_RATE_DATES}
timeZone="America/New_York" initialValues={{}} ... /> so the behavior of
FormItemTableWrapper's total calculation is actually verified.

Comment on lines +668 to +727
it("highlights early_bird rate when current time is before early_bird_rate", () => {
jest
.spyOn(Date, "now")
.mockImplementation(() => MOCK_TIME_BEFORE_EARLY_BIRD);

render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);

expect(screen.getAllByText("$150.00").length).toBeGreaterThan(0);
});

it("highlights standard rate when current time is between early_bird and standard", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.early_bird_rate + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);

render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);

expect(screen.getAllByText("$188.00").length).toBeGreaterThan(0);
});

it("highlights onsite rate when current time is after standard_rate", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.standard_rate + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);

render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);

expect(screen.getAllByText("$224.00").length).toBeGreaterThan(0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Rate-highlighting tests are currently false positives.

The assertions only check that $150.00 / $188.00 / $224.00 exist, but those values are always present in rate columns regardless of highlight/applicable-rate logic. These tests won’t catch regressions in highlighting behavior.

🔧 Tighten the assertions to verify highlighted/applicable behavior
-      expect(screen.getAllByText("$188.00").length).toBeGreaterThan(0);
+      // Assert a highlight-specific signal instead of static text presence.
+      // Example: if highlighted rates are rendered with a dedicated class/testid:
+      // expect(screen.getByTestId("rate-standard-highlighted")).toBeInTheDocument();
+      // Or assert computed total driven by applicable rate for a controlled quantity setup.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 668 - 727, The tests are false positives because they only assert that the
rate text exists; instead, locate the DOM node for the specific rate rendered by
FormItemTableWrapper (use getByText/getAllByText for
"$150.00"/"$188.00"/"$224.00" then navigate to its cell/parent) and assert that
that cell has the component's highlight indicator (the CSS class or data
attribute that marks the applicable rate in FormItemTable, e.g., a class like
"applicable" / "applicable-rate" or a data attribute like "data-applicable");
update the three specs that mock Date.now (using MOCK_TIME_BEFORE_EARLY_BIRD,
MOCK_RATE_DATES with TIME_OFFSET and MILLISECONDS_MULTIPLIER) to assert the
presence of that highlight marker on the expected rate cell rather than just the
text existing.

Comment on lines +686 to +715
it("highlights standard rate when current time is between early_bird and standard", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.early_bird_rate + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);

render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);

expect(screen.getAllByText("$188.00").length).toBeGreaterThan(0);
});

it("highlights onsite rate when current time is after standard_rate", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.standard_rate + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the correct rateDates keys in mocked timestamps.

Line 691 and Line 713 reference non-existent keys (early_bird_rate, standard_rate). That makes Date.now() return NaN in these tests, so the intended rate-window behavior is not actually being exercised.

🔧 Proposed fix
       jest
         .spyOn(Date, "now")
         .mockImplementation(
           () =>
-            (MOCK_RATE_DATES.early_bird_rate + TIME_OFFSET) *
+            (MOCK_RATE_DATES.early_bird_end_date + TIME_OFFSET) *
             MILLISECONDS_MULTIPLIER
         );
...
       jest
         .spyOn(Date, "now")
         .mockImplementation(
           () =>
-            (MOCK_RATE_DATES.standard_rate + TIME_OFFSET) *
+            (MOCK_RATE_DATES.standard_price_end_date + TIME_OFFSET) *
             MILLISECONDS_MULTIPLIER
         );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("highlights standard rate when current time is between early_bird and standard", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.early_bird_rate + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);
render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);
expect(screen.getAllByText("$188.00").length).toBeGreaterThan(0);
});
it("highlights onsite rate when current time is after standard_rate", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.standard_rate + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);
it("highlights standard rate when current time is between early_bird and standard", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.early_bird_end_date + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);
render(
<FormItemTableWrapper
data={MOCK_FORM_A.items}
rateDates={MOCK_RATE_DATES}
timeZone="America/New_York"
onNotesClick={mockOnNotesClick}
onSettingsClick={mockOnSettingsClick}
/>
);
expect(screen.getAllByText("$188.00").length).toBeGreaterThan(0);
});
it("highlights onsite rate when current time is after standard_rate", () => {
jest
.spyOn(Date, "now")
.mockImplementation(
() =>
(MOCK_RATE_DATES.standard_price_end_date + TIME_OFFSET) *
MILLISECONDS_MULTIPLIER
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/__tests__/FormItemTable.test.js` around
lines 686 - 715, The tests "highlights standard rate when current time is
between early_bird and standard" and "highlights onsite rate when current time
is after standard_rate" are using non-existent MOCK_RATE_DATES keys
(early_bird_rate, standard_rate) which causes Date.now() to produce NaN; update
the jest.spyOn(Date, "now").mockImplementation calls to reference the correct
keys on MOCK_RATE_DATES (e.g., MOCK_RATE_DATES.early_bird and
MOCK_RATE_DATES.standard or whatever exact key names exist on MOCK_RATE_DATES)
so the mocked timestamps compute valid numbers and the rate-window behavior in
the FormItemTable tests is actually exercised.

Comment on lines +233 to +238
test("renders close button", () => {
renderWithStore();

const closeButton = screen.getByRole("button", { name: "" });
expect(closeButton).toBeInTheDocument();
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the test file
find . -name "select-form-dialog.test.js" -type f

Repository: fntechgit/summit-admin

Length of output: 170


🏁 Script executed:

# List the select-form-dialog component directory
fd "select-form-dialog" -t d | head -20

Repository: fntechgit/summit-admin

Length of output: 132


🏁 Script executed:

# Get an overview of the repository structure
git ls-files | grep -E "(select-form-dialog|sponsor)" | head -30

Repository: fntechgit/summit-admin

Length of output: 1595


🏁 Script executed:

# Read the test file to see the specific test at lines 233-238
cat -n ./src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js | sed -n '230,240p'

Repository: fntechgit/summit-admin

Length of output: 464


🏁 Script executed:

# Get line count of test file to understand its size
wc -l ./src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js

Repository: fntechgit/summit-admin

Length of output: 174


🏁 Script executed:

# Look for component files in the select-form-dialog directory
ls -la ./src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/

Repository: fntechgit/summit-admin

Length of output: 293


🏁 Script executed:

# Read the component file to verify close button implementation
cat -n ./src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js | head -100

Repository: fntechgit/summit-admin

Length of output: 3163


🏁 Script executed:

# Search for CloseIcon or close button in the component
grep -n "close\|Close\|button" ./src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js -i | head -20

Repository: fntechgit/summit-admin

Length of output: 521


🏁 Script executed:

# Get full context of the close button implementation
cat -n ./src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/index.js | sed -n '115,130p'

Repository: fntechgit/summit-admin

Length of output: 783


Close button lacks accessible label, making the test selector fragile.

The close button (lines 121-123) has no aria-label or accessible text, which is why the test must query with { name: "" }. This creates a fragile test and an accessibility gap. Add an aria-label="close" to the IconButton in the production component, then update the test selector to { name: /close/i }.

Production fix and test update

Production component (index.js, line 121):

-<IconButton size="large" sx={{ p: 0 }} onClick={() => handleClose()}>
+<IconButton size="large" sx={{ p: 0 }} onClick={() => handleClose()} aria-label="close">

Test (lines 236):

-const closeButton = screen.getByRole("button", { name: "" });
+const closeButton = screen.getByRole("button", { name: /close/i });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`
around lines 233 - 238, The close IconButton in the SelectFormDialog component
is missing an accessible label, causing the test in select-form-dialog.test.js
to query with an empty name; add aria-label="close" to the IconButton element in
the SelectFormDialog (component where IconButton is rendered) and then update
the test to select the button with screen.getByRole("button", { name: /close/i
}) (the test uses renderWithStore and assertions on screen.getByRole).

Comment on lines +415 to +432
test("resets selected rows and addon when closing", async () => {
const onClose = jest.fn();
renderWithStore({ onClose });

const radioButtons = screen.getAllByRole("radio");
const addonSelect = screen.getByTestId("addon-select");

// Select form and addon
await userEvent.selectOptions(addonSelect, "1");
await userEvent.click(radioButtons[0]);
expect(screen.getByText("1 items selected")).toBeInTheDocument();

// Close dialog
const closeButton = screen.getByRole("button", { name: "" });
await userEvent.click(closeButton);

expect(onClose).toHaveBeenCalled();
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test doesn't verify the reset behavior it claims to test.

The test is named "resets selected rows and addon when closing" but only asserts that onClose was called. It doesn't verify that the selection count returns to "0 items selected" or that the addon dropdown is cleared.

📝 Suggested improvement to actually verify reset
   test("resets selected rows and addon when closing", async () => {
     const onClose = jest.fn();
-    renderWithStore({ onClose });
+    const { rerender } = renderWithStore({ onClose });
 
     const radioButtons = screen.getAllByRole("radio");
     const addonSelect = screen.getByTestId("addon-select");
 
     // Select form and addon
     await userEvent.selectOptions(addonSelect, "1");
     await userEvent.click(radioButtons[0]);
     expect(screen.getByText("1 items selected")).toBeInTheDocument();
 
     // Close dialog
     const closeButton = screen.getByRole("button", { name: "" });
     await userEvent.click(closeButton);
 
     expect(onClose).toHaveBeenCalled();
+    
+    // Verify state was reset (check UI reflects reset)
+    expect(screen.getByText("0 items selected")).toBeInTheDocument();
+    expect(addonSelect).toHaveValue("");
+    expect(radioButtons[0]).not.toBeChecked();
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/pages/sponsors/sponsor-cart-tab/components/select-form-dialog/__tests__/select-form-dialog.test.js`
around lines 415 - 432, The test "resets selected rows and addon when closing"
currently only asserts onClose was called; after invoking
userEvent.click(closeButton) update the assertions to verify the UI/state reset:
assert the selection count is back to "0 items selected" (e.g.,
expect(screen.getByText("0 items selected")).toBeInTheDocument()) and assert the
addon select is cleared (e.g., expect(addonSelect).toHaveValue("") or query the
option selection). If the dialog unmounts on close, re-open or re-render via
renderWithStore (or call whatever opens the dialog) and then assert radioButtons
selection is cleared and addonSelect has no value. Keep references to onClose,
renderWithStore, radioButtons, addonSelect and closeButton to locate the code to
change.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/mui/formik-inputs/mui-formik-pricefield.js (1)

64-67: ⚠️ Potential issue | 🟡 Minor

PropTypes incomplete for new props.

The inCents and inputProps props are missing from PropTypes validation.

Proposed fix
 MuiFormikPriceField.propTypes = {
   name: PropTypes.string.isRequired,
-  label: PropTypes.string
+  label: PropTypes.string,
+  inCents: PropTypes.bool,
+  inputProps: PropTypes.object
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-pricefield.js` around lines 64 -
67, The PropTypes for the MuiFormikPriceField component are missing validations
for the new props inCents and inputProps; update the
MuiFormikPriceField.propTypes to include inCents: PropTypes.bool (or
PropTypes.oneOfType([PropTypes.bool, PropTypes.number]) if you expect numeric
flags) and inputProps: PropTypes.object (or a more specific
PropTypes.shape({...}) if you know expected keys) alongside the existing name
and label so PropTypes covers all used props.
♻️ Duplicate comments (7)
src/actions/sponsor-cart-actions.js (2)

278-284: ⚠️ Potential issue | 🟠 Major

Normalize discount_amount before submission.

The reducer uses amountFromCents(form.discount_amount) to convert received values (line 74 in reducer), indicating the API expects cents/basis points. The current code sends the raw form value without conversion, which will result in incorrect discount values.

🛠️ Suggested fix
+const amountToCents = (value) => Math.round((value || 0) * 100);
+
 const normalizedEntity = {
   form_id: formId,
   addon_id: addOnId,
   discount_type: formValues.discount_type,
-  discount_amount: formValues.discount_amount,
+  discount_amount: amountToCents(formValues.discount_amount),
   items: normalizeItems(formValues.items)
 };

Alternatively, if there's a shared utility function for this conversion, use that instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 278 - 284, The submitted
normalizedEntity currently passes formValues.discount_amount raw; convert this
to the API expected integer (cents/basis points) before assigning to
normalizedEntity.discount_amount — e.g., use the shared conversion utility if
available (or multiply by 100) so the value matches the reducer's
amountFromCents convention; update the code that builds normalizedEntity (the
block that creates normalizedEntity and references formValues.discount_amount
and normalizeItems) to perform this conversion.

294-299: ⚠️ Potential issue | 🟡 Minor

Use appropriate success message for cart form operations.

The translation key sponsor_list.sponsor_added is intended for sponsor creation, not cart form operations. This will display a misleading success message to users.

🛠️ Suggested fix
       dispatch(
         snackbarSuccessHandler({
           title: T.translate("general.success"),
-          html: T.translate("sponsor_list.sponsor_added")
+          html: T.translate("sponsor_forms.form_saved_success")
         })
       );

Note: Verify the correct translation key exists or add it to the i18n files.

#!/bin/bash
# Check for existing cart/form success translation keys
echo "=== Search for form-related success translation keys ==="
rg -n "form.*success\|cart.*success\|saved.*success" src/i18n/ -i
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 294 - 299, The success
snackbar is using the wrong translation key (sponsor_list.sponsor_added) for
cart form operations; in the dispatch call that uses snackbarSuccessHandler (the
block dispatch(snackbarSuccessHandler({ title: T.translate(...), html:
T.translate("sponsor_list.sponsor_added") }))), replace the html translation key
with the correct cart/form-specific key (for example cart.form_saved or
sponsor_cart.form_saved) and, if that key does not exist, add it to the i18n
files; ensure the title still uses T.translate("general.success") and run the
suggested i18n search to confirm or create the proper translation entry.
src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js (4)

189-192: ⚠️ Potential issue | 🟠 Major

Discount validation is bound to the wrong field name.

The schema defines discount, but the form input uses discount_amount (line 147, and in FormItemTable). The discount input will not be validated by Yup.

Proposed fix
-  schema.discount = yup.number(T.translate("validation.number"));
+  schema.discount_amount = yup
+    .number(T.translate("validation.number"))
+    .min(0, T.translate("validation.minimum", { minimum: 0 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 189 - 192, The Yup schema currently validates "discount" (schema.discount)
but the form field is named "discount_amount" (used in the form and
FormItemTable), so the discount input isn't being validated; update the schema
to validate schema.discount_amount instead of schema.discount (or add
schema.discount_amount alongside schema.discount if both are needed), ensuring
the Yup rules (yup.number(...)/.nullable() and discount_type mapping) match the
form field names used in the component.

155-160: ⚠️ Potential issue | 🟡 Minor

Use forEach instead of map for side-effect-only iteration.

Same issue as in buildInitialValues — the map() callback only mutates acc without returning a value.

Proposed fix
     item.meta_fields
       .filter((f) => f.class_field === "Form")
-      .map((f) => {
+      .forEach((f) => {
         acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
           getYupValidation(f);
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 155 - 160, Replace the side-effect-only .map call over item.meta_fields
with .forEach so you don't rely on map's return value; in the block that
currently does item.meta_fields.filter(...).map((f) => {
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
getYupValidation(f); }); use .forEach instead and keep the same body mutating
acc (same pattern as buildInitialValues) to populate validation entries for
getYupValidation results.

166-169: ⚠️ Potential issue | 🟡 Minor

Minimum validation message interpolation is malformed.

The validation message concatenates the translated string with " 1" instead of using interpolation, leaving the {minimum} placeholder unresolved.

Proposed fix
     let globalQtySchema = yup
       .number(T.translate("validation.number"))
-      .min(1, `${T.translate("validation.minimum")} 1`);
+      .min(1, T.translate("validation.minimum", { minimum: 1 }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 166 - 169, The yup minimum validator message is built by concatenating the
translated string with " 1" which leaves the {minimum} placeholder unresolved;
update the validator used in globalQtySchema (the yup.number().min call) to pass
the minimum value into the translation interpolation instead of string
concatenation — e.g., call T.translate("validation.minimum", { minimum: 1 }) (or
use the appropriate interpolation API your i18n uses) and supply that as the
second argument to .min(1, ...) so the message renders correctly when
item.quantity_limit_per_sponsor > 0.

131-134: ⚠️ Potential issue | 🟡 Minor

Use forEach instead of map for side-effect-only iteration.

The map() callback mutates acc without returning a value. Since the mapped array is discarded, forEach() is semantically correct and resolves the Biome linting error.

Proposed fix
-    item.meta_fields.map((f) => {
+    item.meta_fields.forEach((f) => {
       acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
         parseValue(f, timeZone);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 131 - 134, The code is using item.meta_fields.map(...) purely for
side-effects by mutating acc, which triggers a lint error; change the iteration
to use forEach instead of map so the callback's non-returning side-effects are
semantically correct — update the block where item.meta_fields is iterated (the
callback that sets
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = parseValue(f,
timeZone)) to use item.meta_fields.forEach(...) while leaving parseValue, acc,
and the key construction unchanged.
src/components/mui/FormItemTable/index.js (1)

72-86: ⚠️ Potential issue | 🟡 Minor

Missing extraColumns in dependency array.

The calculateQuantity callback reads extraColumns but only lists valuesStr in its dependency array. If extraColumns changes (e.g., different form data loaded), the callback won't update correctly.

Proposed fix
   const calculateQuantity = useCallback(
     (row) => {
       const qtyEXC = extraColumns.filter((exc) => exc.type === "Quantity");
       return qtyEXC.reduce((res, exc) => {
         const start = res > 0 ? res : 1;
         return (
           start *
           (values?.[
             `i-${row.form_item_id}-c-${exc.class_field}-f-${exc.type_id}`
           ] || 0)
         );
       }, 0);
     },
-    [valuesStr]
+    [valuesStr, extraColumns]
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 72 - 86, The
calculateQuantity useCallback currently depends only on valuesStr but also reads
extraColumns; update the dependency array of useCallback to include extraColumns
(and keep valuesStr) so calculateQuantity recomputes when extraColumns changes;
locate the calculateQuantity declaration in this file (the useCallback wrapping
function named calculateQuantity) and add extraColumns to its dependency array.
🧹 Nitpick comments (3)
src/reducers/sponsors/sponsor-page-cart-list-reducer.js (1)

48-51: Consider pluralizing item count for better UX.

Minor: The string "1 items" reads awkwardly. Consider using proper pluralization.

♻️ Optional enhancement
 const mapForm = (formData) => ({
   ...formData,
-  item_count: `${formData.items.length} items`
+  item_count: `${formData.items.length} ${formData.items.length === 1 ? "item" : "items"}`
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/reducers/sponsors/sponsor-page-cart-list-reducer.js` around lines 48 -
51, The mapForm function currently builds item_count as
`${formData.items.length} items`, which yields "1 items"; update mapForm to
pluralize correctly by checking formData.items.length and using "item" when
length === 1 and "items" otherwise (or use a small helper like pluralizeCount),
ensuring item_count becomes e.g. "1 item" or "2 items" while keeping the same
mapForm signature and behavior.
src/components/mui/formik-inputs/mui-formik-discountfield.js (1)

76-79: PropTypes incomplete for required props.

discountType is used to determine the adornment and input constraints but is missing from PropTypes. inCents also has a default value but isn't documented.

📝 Proposed fix to complete PropTypes
 MuiFormikDiscountField.propTypes = {
   name: PropTypes.string.isRequired,
-  label: PropTypes.string
+  label: PropTypes.string,
+  discountType: PropTypes.oneOf([DISCOUNT_TYPES.AMOUNT, DISCOUNT_TYPES.RATE]).isRequired,
+  inCents: PropTypes.bool
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-discountfield.js` around lines 76
- 79, Add missing PropTypes for the MuiFormikDiscountField component: include
discountType (string, required if component depends on it) and inCents (bool) in
MuiFormikDiscountField.propTypes and document default value by ensuring
defaultProps includes inCents if not already present; update the propTypes block
to list name, label, discountType, and inCents and add defaultProps for inCents
to reflect its default behavior used by the component.
src/components/mui/FormItemTable/index.js (1)

198-216: Opacity values use inconsistent types.

The opacity values mix numbers (1) with strings ("38%"). While MUI's sx prop handles both, using consistent numeric values improves readability and avoids potential edge cases.

Suggested consistency fix
               <TableCell
                 sx={{
-                  opacity: currentApplicableRate === "early_bird" ? 1 : "38%"
+                  opacity: currentApplicableRate === "early_bird" ? 1 : 0.38
                 }}
               >

Apply similar changes to lines 207 and 213.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 198 - 216, The
TableCell sx opacity values mix a number (1) with string percentages ("38%");
update the three TableCell sx definitions (the ones whose opacity depends on
currentApplicableRate for "early_bird", "standard", and "onsite") to use numeric
opacity 0.38 instead of the string "38%" so all branches use numeric values
(e.g., opacity: currentApplicableRate === "early_bird" ? 1 : 0.38).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/actions/sponsor-cart-actions.js`:
- Around line 220-222: The final .then() call that dispatches stopLoading() can
be skipped on errors; change the promise chain that currently does
")(params)(dispatch).then(() => { dispatch(stopLoading()); });" to use
.finally(() => { dispatch(stopLoading()); }); so stopLoading is always
dispatched regardless of success or failure (this is the same pattern used
elsewhere in this file and should be applied to the thunk invocation that
follows authErrorHandler).
- Around line 242-244: The promise chain that currently calls
dispatch(stopLoading()) inside a .then() may not run on rejection; change the
finalization to use .finally() so stopLoading() is always dispatched regardless
of success or failure—replace the .then(() => { dispatch(stopLoading()); })
attached to the invoked thunk (the )(params)(dispatch) promise) with .finally(()
=> { dispatch(stopLoading()); }) to ensure reliable cleanup.
- Around line 247-263: In normalizeItems, custom_rate is left in dollars but the
API expects cents; update the inclusion so when custom_rate > 0 you pass
custom_rate: amountToCents(custom_rate) instead of the dollar value, and add an
import for amountToCents from "openstack-uicore-foundation/lib/utils/money";
keep the existing logic for quantity, meta_fields and hasQtyFields in the
normalizeItems function unchanged.

In `@src/components/mui/formik-inputs/mui-formik-discountfield.js`:
- Around line 57-63: The HTML input attributes (min, max, step, inputMode) are
currently placed under slotProps.input in mui-formik-discountfield.js but must
live under slotProps.htmlInput like in mui-formik-textfield.js; update the
slotProps object so that adornment and other MUI Input props remain in
slotProps.input while moving min, max, step, inputMode (and any other raw HTML
attributes) into slotProps.htmlInput, preserving spread order of ...adornment
and ...inputProps so the correct props apply to the MUI Input vs the underlying
HTML input.
- Around line 23-26: The local state initialized from initialValue (computed
from field.value and amountFromCents) won't update if Formik changes
field.value; update the component to sync local state with Formik by adding a
useEffect that watches field.value and inCents and calls
setValue(amountFromCents(field.value) or field.value) when they change, ensuring
the local value, the useState(value,setValue) pair, and any displayed input stay
in sync with external setFieldValue/resets.

In `@src/components/mui/formik-inputs/mui-formik-pricefield.js`:
- Around line 22-25: The local state initialized by const initialValue = inCents
? amountFromCents(field.value || 0) : field.value and useState(initialValue) in
mui-formik-pricefield.js (and similarly in mui-formik-discountfield.js) becomes
stale when Formik updates the field; add a useEffect that watches field.value
and inCents, computes the same newValue using amountFromCents(field.value || 0)
when inCents is true (or field.value when false), and calls setValue(newValue)
so the component state (value) stays in sync with Formik updates and
resetForm/setFieldValue calls.

In `@src/components/mui/FormItemTable/index.js`:
- Around line 101-109: Update the misleading comment inside the totalAmount
useMemo where discount is computed: clarify that when values.discount_type is
not DISCOUNT_TYPES.AMOUNT the code converts values.discount_amount (stored in
cents for a percentage) into a decimal fraction for the percentage calculation
by dividing by ONE_HUNDRED * ONE_HUNDRED (10,000). Edit the comment near the
discount calculation (referencing totalAmount, useMemo, values.discount_type,
values.discount_amount, and ONE_HUNDRED) to say something like "// convert
percentage (stored in cents) to decimal fraction" so it accurately describes the
operation.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js`:
- Around line 270-282: The useFormik call incorrectly passes a second argument
([form?.items]) which is not supported; remove the second argument and rely on
enableReinitialize: true so the form reinitializes when buildInitialValues(form,
showTimeZone) changes; update the call to only pass the single config object
(containing initialValues built via buildInitialValues, validationSchema from
buildValidationSchema, onSubmit calling handleSave, and enableReinitialize:
true) to useFormik.

---

Outside diff comments:
In `@src/components/mui/formik-inputs/mui-formik-pricefield.js`:
- Around line 64-67: The PropTypes for the MuiFormikPriceField component are
missing validations for the new props inCents and inputProps; update the
MuiFormikPriceField.propTypes to include inCents: PropTypes.bool (or
PropTypes.oneOfType([PropTypes.bool, PropTypes.number]) if you expect numeric
flags) and inputProps: PropTypes.object (or a more specific
PropTypes.shape({...}) if you know expected keys) alongside the existing name
and label so PropTypes covers all used props.

---

Duplicate comments:
In `@src/actions/sponsor-cart-actions.js`:
- Around line 278-284: The submitted normalizedEntity currently passes
formValues.discount_amount raw; convert this to the API expected integer
(cents/basis points) before assigning to normalizedEntity.discount_amount —
e.g., use the shared conversion utility if available (or multiply by 100) so the
value matches the reducer's amountFromCents convention; update the code that
builds normalizedEntity (the block that creates normalizedEntity and references
formValues.discount_amount and normalizeItems) to perform this conversion.
- Around line 294-299: The success snackbar is using the wrong translation key
(sponsor_list.sponsor_added) for cart form operations; in the dispatch call that
uses snackbarSuccessHandler (the block dispatch(snackbarSuccessHandler({ title:
T.translate(...), html: T.translate("sponsor_list.sponsor_added") }))), replace
the html translation key with the correct cart/form-specific key (for example
cart.form_saved or sponsor_cart.form_saved) and, if that key does not exist, add
it to the i18n files; ensure the title still uses T.translate("general.success")
and run the suggested i18n search to confirm or create the proper translation
entry.

In `@src/components/mui/FormItemTable/index.js`:
- Around line 72-86: The calculateQuantity useCallback currently depends only on
valuesStr but also reads extraColumns; update the dependency array of
useCallback to include extraColumns (and keep valuesStr) so calculateQuantity
recomputes when extraColumns changes; locate the calculateQuantity declaration
in this file (the useCallback wrapping function named calculateQuantity) and add
extraColumns to its dependency array.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js`:
- Around line 189-192: The Yup schema currently validates "discount"
(schema.discount) but the form field is named "discount_amount" (used in the
form and FormItemTable), so the discount input isn't being validated; update the
schema to validate schema.discount_amount instead of schema.discount (or add
schema.discount_amount alongside schema.discount if both are needed), ensuring
the Yup rules (yup.number(...)/.nullable() and discount_type mapping) match the
form field names used in the component.
- Around line 155-160: Replace the side-effect-only .map call over
item.meta_fields with .forEach so you don't rely on map's return value; in the
block that currently does item.meta_fields.filter(...).map((f) => {
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
getYupValidation(f); }); use .forEach instead and keep the same body mutating
acc (same pattern as buildInitialValues) to populate validation entries for
getYupValidation results.
- Around line 166-169: The yup minimum validator message is built by
concatenating the translated string with " 1" which leaves the {minimum}
placeholder unresolved; update the validator used in globalQtySchema (the
yup.number().min call) to pass the minimum value into the translation
interpolation instead of string concatenation — e.g., call
T.translate("validation.minimum", { minimum: 1 }) (or use the appropriate
interpolation API your i18n uses) and supply that as the second argument to
.min(1, ...) so the message renders correctly when
item.quantity_limit_per_sponsor > 0.
- Around line 131-134: The code is using item.meta_fields.map(...) purely for
side-effects by mutating acc, which triggers a lint error; change the iteration
to use forEach instead of map so the callback's non-returning side-effects are
semantically correct — update the block where item.meta_fields is iterated (the
callback that sets
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = parseValue(f,
timeZone)) to use item.meta_fields.forEach(...) while leaving parseValue, acc,
and the key construction unchanged.

---

Nitpick comments:
In `@src/components/mui/formik-inputs/mui-formik-discountfield.js`:
- Around line 76-79: Add missing PropTypes for the MuiFormikDiscountField
component: include discountType (string, required if component depends on it)
and inCents (bool) in MuiFormikDiscountField.propTypes and document default
value by ensuring defaultProps includes inCents if not already present; update
the propTypes block to list name, label, discountType, and inCents and add
defaultProps for inCents to reflect its default behavior used by the component.

In `@src/components/mui/FormItemTable/index.js`:
- Around line 198-216: The TableCell sx opacity values mix a number (1) with
string percentages ("38%"); update the three TableCell sx definitions (the ones
whose opacity depends on currentApplicableRate for "early_bird", "standard", and
"onsite") to use numeric opacity 0.38 instead of the string "38%" so all
branches use numeric values (e.g., opacity: currentApplicableRate ===
"early_bird" ? 1 : 0.38).

In `@src/reducers/sponsors/sponsor-page-cart-list-reducer.js`:
- Around line 48-51: The mapForm function currently builds item_count as
`${formData.items.length} items`, which yields "1 items"; update mapForm to
pluralize correctly by checking formData.items.length and using "item" when
length === 1 and "items" otherwise (or use a small helper like pluralizeCount),
ensuring item_count becomes e.g. "1 item" or "2 items" while keeping the same
mapForm signature and behavior.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2573190 and 1177cc4.

📒 Files selected for processing (8)
  • src/actions/sponsor-cart-actions.js
  • src/components/mui/FormItemTable/__tests__/FormItemTable.test.js
  • src/components/mui/FormItemTable/index.js
  • src/components/mui/formik-inputs/mui-formik-discountfield.js
  • src/components/mui/formik-inputs/mui-formik-pricefield.js
  • src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js
  • src/reducers/sponsors/sponsor-page-cart-list-reducer.js
  • src/utils/constants.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/utils/constants.js
  • src/components/mui/FormItemTable/tests/FormItemTable.test.js

Comment on lines +220 to +222
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use .finally() to ensure loading state is cleared on error.

If the request fails after authErrorHandler runs, stopLoading won't be called, potentially leaving the UI in a loading state. Other functions in this file use .finally() for this purpose.

🛠️ Suggested fix
-    )(params)(dispatch).then(() => {
-      dispatch(stopLoading());
-    });
+    )(params)(dispatch)
+      .catch((err) => {
+        console.error(err);
+      })
+      .finally(() => {
+        dispatch(stopLoading());
+      });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
)(params)(dispatch)
.catch((err) => {
console.error(err);
})
.finally(() => {
dispatch(stopLoading());
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 220 - 222, The final
.then() call that dispatches stopLoading() can be skipped on errors; change the
promise chain that currently does ")(params)(dispatch).then(() => {
dispatch(stopLoading()); });" to use .finally(() => { dispatch(stopLoading());
}); so stopLoading is always dispatched regardless of success or failure (this
is the same pattern used elsewhere in this file and should be applied to the
thunk invocation that follows authErrorHandler).

Comment on lines +242 to +244
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same issue: Use .finally() for reliable loading state cleanup.

Consistent with the previous comment, replace .then() with .finally() to ensure stopLoading is called regardless of success or failure.

🛠️ Suggested fix
-  )(params)(dispatch).then(() => {
-    dispatch(stopLoading());
-  });
+  )(params)(dispatch)
+    .catch((err) => {
+      console.error(err);
+    })
+    .finally(() => {
+      dispatch(stopLoading());
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 242 - 244, The promise
chain that currently calls dispatch(stopLoading()) inside a .then() may not run
on rejection; change the finalization to use .finally() so stopLoading() is
always dispatched regardless of success or failure—replace the .then(() => {
dispatch(stopLoading()); }) attached to the invoked thunk (the
)(params)(dispatch) promise) with .finally(() => { dispatch(stopLoading()); })
to ensure reliable cleanup.

Comment on lines +247 to +263
const normalizeItems = (items) =>
items.map((item) => {
const { quantity, custom_rate, ...normalizedItem } = item;
const hasQtyFields = item.meta_fields.some(
(f) => f.class_field === "Form" && f.type_name === "Quantity"
);
const metaFields = item.meta_fields.filter(
(item) => item.current_value !== null
);

return {
...normalizedItem,
...(hasQtyFields ? {} : { quantity }),
...(custom_rate > 0 ? { custom_rate } : {}),
meta_fields: metaFields
};
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Searching for custom_rate usage in form/page components ==="
rg -n "custom_rate" src/ --type=js -B 2 -A 2 | head -100

echo ""
echo "=== Looking for currency conversion utilities ==="
rg -n "amountToCents|toCents|dollarsTo|centsToDollars|currencyAmount" src/utils/ src/lib/ --type=js

echo ""
echo "=== Checking sponsor-cart-actions.js reducer line 88 for context ==="
sed -n '80,100p' src/actions/sponsor-cart-actions.js

Repository: fntechgit/summit-admin

Length of output: 5249


🏁 Script executed:

#!/bin/bash
echo "=== Finding MuiFormikPriceField implementation ==="
rg -n "const MuiFormikPriceField|function MuiFormikPriceField|export.*MuiFormikPriceField" src/ --type=js -A 10

echo ""
echo "=== Checking where normalizeItems is called ==="
rg -n "normalizeItems\(" src/actions/sponsor-cart-actions.js -B 5 -A 5

echo ""
echo "=== Finding currencyAmountFromCents definition ==="
rg -n "currencyAmountFromCents|amountFromCents" src/ --type=js -B 2 -A 3 | head -50

Repository: fntechgit/summit-admin

Length of output: 6032


🏁 Script executed:

#!/bin/bash
echo "=== Looking for conversion functions to cents ==="
rg -n "amountToCents|toCents|inCents.*true" src/ --type=js

echo ""
echo "=== Check MuiFormikPriceField full implementation for conversion logic ==="
cat -n src/components/mui/formik-inputs/mui-formik-pricefield.js

Repository: fntechgit/summit-admin

Length of output: 4094


Convert custom_rate to cents before submission.

The custom_rate field in the form stores dollars (MuiFormikPriceField without inCents prop), but the API expects cents. The reducer converts FROM cents on read, so normalizeItems must convert TO cents on write. Use amountToCents() before including custom_rate in the submission payload:

Suggested fix
...(custom_rate > 0 ? { custom_rate: amountToCents(custom_rate) } : {})

Remember to import amountToCents from "openstack-uicore-foundation/lib/utils/money".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-cart-actions.js` around lines 247 - 263, In
normalizeItems, custom_rate is left in dollars but the API expects cents; update
the inclusion so when custom_rate > 0 you pass custom_rate:
amountToCents(custom_rate) instead of the dollar value, and add an import for
amountToCents from "openstack-uicore-foundation/lib/utils/money"; keep the
existing logic for quantity, meta_fields and hasQtyFields in the normalizeItems
function unchanged.

Comment on lines +23 to +26
const initialValue = inCents
? amountFromCents(field.value || 0)
: field.value;
const [value, setValue] = useState(initialValue);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Local state will not sync with external Formik updates.

The initialValue is computed once at mount and used to initialize local state. If field.value changes externally (e.g., form reset, programmatic setFieldValue), the component's local value state will be stale.

Consider using useEffect to sync when field.value changes, or use the key prop pattern at the parent level to remount when needed.

🔧 Proposed fix to sync local state with Formik
-  const initialValue = inCents
-    ? amountFromCents(field.value || 0)
-    : field.value;
-  const [value, setValue] = useState(initialValue);
+  const transformedFieldValue = inCents
+    ? amountFromCents(field.value || 0)
+    : field.value;
+  const [value, setValue] = useState(transformedFieldValue);
+
+  React.useEffect(() => {
+    const newValue = inCents
+      ? amountFromCents(field.value || 0)
+      : field.value;
+    setValue(newValue);
+  }, [field.value, inCents]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-discountfield.js` around lines 23
- 26, The local state initialized from initialValue (computed from field.value
and amountFromCents) won't update if Formik changes field.value; update the
component to sync local state with Formik by adding a useEffect that watches
field.value and inCents and calls setValue(amountFromCents(field.value) or
field.value) when they change, ensuring the local value, the
useState(value,setValue) pair, and any displayed input stay in sync with
external setFieldValue/resets.

Comment on lines +57 to +63
slotProps={{
input: {
...adornment,
min: 0,
...inputProps
}
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

HTML input attributes placed in wrong slot.

The min, max, step, and inputMode are HTML input attributes that should be in slotProps.htmlInput, not slotProps.input. As shown in mui-formik-textfield.js, HTML input attributes like maxLength are placed in htmlInput. The input slot is for the MUI Input component props (like adornments).

🔧 Proposed fix to use correct slot structure
       slotProps={{
         input: {
-          ...adornment,
-          min: 0,
-          ...inputProps
-        }
+          ...adornment
+        },
+        htmlInput: {
+          min: 0,
+          ...inputProps
+        }
       }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
slotProps={{
input: {
...adornment,
min: 0,
...inputProps
}
}}
slotProps={{
input: {
...adornment
},
htmlInput: {
min: 0,
...inputProps
}
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-discountfield.js` around lines 57
- 63, The HTML input attributes (min, max, step, inputMode) are currently placed
under slotProps.input in mui-formik-discountfield.js but must live under
slotProps.htmlInput like in mui-formik-textfield.js; update the slotProps object
so that adornment and other MUI Input props remain in slotProps.input while
moving min, max, step, inputMode (and any other raw HTML attributes) into
slotProps.htmlInput, preserving spread order of ...adornment and ...inputProps
so the correct props apply to the MUI Input vs the underlying HTML input.

Comment on lines +22 to +25
const initialValue = inCents
? amountFromCents(field.value || 0)
: field.value;
const [value, setValue] = useState(initialValue);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, explore the repository structure
find . -type f -name "*.js" | grep -E "(formik|form)" | head -20

Repository: fntechgit/summit-admin

Length of output: 988


🏁 Script executed:

#!/bin/bash
# Check the file mentioned in the review
cat -n src/components/mui/formik-inputs/mui-formik-pricefield.js

Repository: fntechgit/summit-admin

Length of output: 2277


🏁 Script executed:

#!/bin/bash
# Search for resetForm or setFieldValue usage
rg -n "resetForm|setFieldValue" --type=js -C3 | head -100

Repository: fntechgit/summit-admin

Length of output: 8267


🏁 Script executed:

#!/bin/bash
# Check if the same pattern exists in mui-formik-discountfield.js
cat -n src/components/mui/formik-inputs/mui-formik-discountfield.js

Repository: fntechgit/summit-admin

Length of output: 2718


Local state becomes stale when Formik field is reset or updated externally.

useState(initialValue) captures field.value only at mount. When formik.resetForm() or setFieldValue() is called elsewhere, the local state doesn't sync, displaying stale values until the user interacts with the field.

Add a useEffect to sync when field.value changes:

useEffect(() => {
  const newValue = inCents ? amountFromCents(field.value || 0) : field.value;
  setValue(newValue);
}, [field.value, inCents]);

The same pattern exists in mui-formik-discountfield.js and needs the same fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-pricefield.js` around lines 22 -
25, The local state initialized by const initialValue = inCents ?
amountFromCents(field.value || 0) : field.value and useState(initialValue) in
mui-formik-pricefield.js (and similarly in mui-formik-discountfield.js) becomes
stale when Formik updates the field; add a useEffect that watches field.value
and inCents, computes the same newValue using amountFromCents(field.value || 0)
when inCents is true (or field.value when false), and calls setValue(newValue)
so the component state (value) stays in sync with Formik updates and
resetForm/setFieldValue calls.

Comment on lines +101 to +109
const totalAmount = useMemo(() => {
const subtotal = data.reduce((acc, row) => acc + calculateRowTotal(row), 0);
const discount =
values.discount_type === DISCOUNT_TYPES.AMOUNT
? values.discount_amount
: subtotal * (values.discount_amount / ONE_HUNDRED / ONE_HUNDRED); // bps to fraction

return subtotal - Math.round(discount);
}, [data, valuesStr]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n "discount_amount" --type=js -C 3

Repository: fntechgit/summit-admin

Length of output: 8247


🏁 Script executed:

# Also check what ONE_HUNDRED is defined as
rg -n "ONE_HUNDRED" --type=js -C 2

Repository: fntechgit/summit-admin

Length of output: 1138


🏁 Script executed:

# Check the discount_type values and DISCOUNT_TYPES constant
rg -n "DISCOUNT_TYPES" --type=js -C 3

Repository: fntechgit/summit-admin

Length of output: 7028


🏁 Script executed:

# Look for the FormItemTable component and its surrounding context
fd "FormItemTable" --type f

Repository: fntechgit/summit-admin

Length of output: 130


🏁 Script executed:

cat -n src/components/mui/formik-inputs/mui-formik-discountfield.js

Repository: fntechgit/summit-admin

Length of output: 2718


🏁 Script executed:

# Check the money utility functions to understand amountFromCents and amountToCents
rg -n "amountFromCents|amountToCents" --type=js -C 2

Repository: fntechgit/summit-admin

Length of output: 16327


🏁 Script executed:

# Look at the test file to understand expected behavior
cat -n src/components/mui/FormItemTable/__tests__/FormItemTable.test.js | head -100

Repository: fntechgit/summit-admin

Length of output: 3205


🏁 Script executed:

rg -n "discount" src/components/mui/FormItemTable/__tests__/FormItemTable.test.js -C 5

Repository: fntechgit/summit-admin

Length of output: 341


🏁 Script executed:

# Let's also check where MuiFormikDiscountField is actually used in the edit-form
rg -n "MuiFormikDiscountField" --type=js -C 5

Repository: fntechgit/summit-admin

Length of output: 3693


🏁 Script executed:

# Check the edit-form to see how discount_amount is initialized and used
sed -n '145,160p' src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js

Repository: fntechgit/summit-admin

Length of output: 536


🏁 Script executed:

sed -n '273,285p' src/components/mui/FormItemTable/index.js

Repository: fntechgit/summit-admin

Length of output: 438


🏁 Script executed:

# Check if there's any place where discount_amount gets converted before being passed to FormItemTable
rg -n "inCents" src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js -C 3

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Let's search for where FormItemTable is instantiated to see what values are passed
rg -n "FormItemTable" src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js -B 5 -A 10

Repository: fntechgit/summit-admin

Length of output: 1436


🏁 Script executed:

sed -n '256,275p' src/components/mui/FormItemTable/__tests__/FormItemTable.test.js

Repository: fntechgit/summit-admin

Length of output: 441


🏁 Script executed:

# Look for discount-related test cases
grep -n "discount" src/components/mui/FormItemTable/__tests__/FormItemTable.test.js

Repository: fntechgit/summit-admin

Length of output: 126


🏁 Script executed:

rg -n "calculateRowTotal" src/components/mui/FormItemTable/index.js -B 2 -A 10

Repository: fntechgit/summit-admin

Length of output: 1548


🏁 Script executed:

# Also check what format rates are in (cents or dollars)
sed -n '36,50p' src/components/mui/FormItemTable/__tests__/FormItemTable.test.js

Repository: fntechgit/summit-admin

Length of output: 357


🏁 Script executed:

# Verify the external library's amountToCents behavior through documentation or usage patterns
rg -n "amountToCents" src/actions/ -A 1 -B 1 | head -30

Repository: fntechgit/summit-admin

Length of output: 1979


Update the misleading discount rate conversion comment.

The comment "bps to fraction" inaccurately describes the operation. When discount_type is DISCOUNT_TYPES.RATE, discount_amount is stored in cents (0-100 stored as 0-10000 cents). Dividing by ONE_HUNDRED / ONE_HUNDRED (10,000) converts this back to a decimal fraction for percentage calculation. Change the comment to clarify: // convert percentage (stored in cents) to decimal fraction or similar.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/FormItemTable/index.js` around lines 101 - 109, Update the
misleading comment inside the totalAmount useMemo where discount is computed:
clarify that when values.discount_type is not DISCOUNT_TYPES.AMOUNT the code
converts values.discount_amount (stored in cents for a percentage) into a
decimal fraction for the percentage calculation by dividing by ONE_HUNDRED *
ONE_HUNDRED (10,000). Edit the comment near the discount calculation
(referencing totalAmount, useMemo, values.discount_type, values.discount_amount,
and ONE_HUNDRED) to say something like "// convert percentage (stored in cents)
to decimal fraction" so it accurately describes the operation.

Comment on lines +270 to +282
const formik = useFormik(
{
initialValues: buildInitialValues(form, showTimeZone),
validationSchema: yup.object({
...buildValidationSchema(form?.items || [])
}),
onSubmit: (values) => {
handleSave(values);
},
enableReinitialize: true
},
[form?.items]
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Formik useFormik hook second argument dependencies array documentation

💡 Result:

Formik’s official docs do not describe (or support) a “second argument dependencies array” for useFormik.

  • Official API signature: useFormik<Values>(config: FormikConfig<Values>): FormikProps<Values> — i.e., one argument only (the config object). [1]
  • Community references that show the TypeScript signature also show only the config parameter, with no dependencies array. [2]

If you saw useFormik(config, deps), it’s either outdated/mistaken info or confusion with a different hook/library (some non-Formik form hooks do accept a dependencies parameter).

Sources:
[1] Formik docs – useFormik() API reference https://formik.org/docs/api/useFormik
[2] Stack Overflow – hook signature shown as single-argument useFormik(config) https://stackoverflow.com/questions/64286815/what-type-is-useformik-in-formik-forms


Remove invalid second argument to useFormik.

useFormik accepts only a single configuration object argument. The second argument [form?.items] is invalid and will be ignored. For re-initialization when initialValues change, rely on enableReinitialize: true (already present in the config).

Proposed fix
   const formik = useFormik(
     {
       initialValues: buildInitialValues(form, showTimeZone),
       validationSchema: yup.object({
         ...buildValidationSchema(form?.items || [])
       }),
       onSubmit: (values) => {
         handleSave(values);
       },
       enableReinitialize: true
-    },
-    [form?.items]
+    }
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 270 - 282, The useFormik call incorrectly passes a second argument
([form?.items]) which is not supported; remove the second argument and rely on
enableReinitialize: true so the form reinitializes when buildInitialValues(form,
showTimeZone) changes; update the call to only pass the single config object
(containing initialValues built via buildInitialValues, validationSchema from
buildValidationSchema, onSubmit calling handleSave, and enableReinitialize:
true) to useFormik.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant